1.83K subscribers
3.3K photos
132 videos
15 files
3.58K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
#prog #rust #моё

Допустим, есть трейт, в интерфейсе которого желательно иметь опциональный метод для оптимизации. Чтобы разговор был более предметным, покажу конкретный пример для иллюстрации излагаемого далее:

trait Push<T> {
fn push_single(&mut self, x: T);
}

impl<T> Push<T> for Vec<T> {
fn push_single(&mut self, x: T) {
self.push(x);
}
}

use std::sync::mpsc::Sender;

impl<T> Push<T> for Sender<T> {
fn push_single(&mut self, x: T) {
self.send(x).unwrap();
}
}

Некоторые из типов, которые реализуют Push, могут зарезервировать память под n следующих элементов (Vec<T>), но не все (Sender<T>). Хотелось бы иметь метод reserve_for_push для этого, но как его интегрировать?

===

Первый способ: добавить метод с реализацией по умолчанию, которая ничего не делает:

trait Push<T> {
fn push_single(&mut self, x: T);
fn reserve_for_push(&mut self, n: usize) {}
}

impl<T> Push<T> for Vec<T> {
// ...
fn reserve_for_push(&mut self, n: usize) {
self.reserve(n);
}
}

// реализация для `Sender<T>` без изменений

Это простой и рабочий метод. Его использование выглядит так:

fn push_vec<T, P: Push<T>>(p: &mut P, xs: Vec<T>) {
p.reserve_for_push(xs.len());
for x in xs {
p.push_single(x);
}
}

С одной стороны, это изменение обратно-совместимо. С другой стороны, так как оно обратно-совместимо, существующие реализации, скорее всего, не будут обновлены для того, чтобы переопределить реализацию по умолчанию (я смотрю на тебя, Clone::clone_from). Также вызывающая сторона не может выяснить, поддерживает ли тип этот метод или нет.

===

Второй способ: дополнительный трейт и специализация:

trait Push<T> { ... }

trait ReserveForPush<T>: Push<T> {
fn reserve_for_push(&mut self, n: usize);
}

impl<T> ReserveForPush<T> for Vec<T> {
fn reserve_for_push(&mut self, n: usize) {
self.reserve(n);
}
}

// остальной код тот же

Вызывающая сторона, которая желает воспользоваться возможной оптимизацией, может сделать это явно через введение трейта, который является точкой специализации:

#![feature(specialization)] // <-- с min_specialization не работает

...

trait ReserveForPushSpec<T>: Push<T> {
fn maybe_reserve(&mut self, n: usize);
}

impl<T, P> ReserveForPushSpec<T> for P
where
P: Push<T>,
{
// неспециализированная реализация
default fn maybe_reserve(&mut self, n: usize) {}
}

impl<T, P> ReserveForPushSpec<T> for P
where
P: ReserveForPush<T>,
{
fn maybe_reserve(&mut self, n: usize) {
self.reserve_for_push(n);
}
}

Теперь вызывающая сторона может либо требовать ReserveForPush и вызывать reserve_for_push для гарантированного наличия метода, либо ReserveForPushSpec и вызывать maybe_reserve для негарантированного наличия оптимизированного метода, но при этом автоматического его использования при его наличии:

fn push_vec<T, P>(p: &mut P, xs: Vec<T>)
where
P: ReserveForPushSpec<T>,
{
p.maybe_reserve(xs.len());
for x in xs {
p.push_single(x);
}
}

fn push_vec_super_duper_fast<T, P>(p: &mut P, xs: Vec<T>)
where
P: ReserveForPush<T>,
{
p.reserve_for_push(xs.len());
for x in xs {
p.push_single(x);
}
}

Разумеется, из-за специализации это сейчас работает лишь на nightly. С другой стороны, это изменение обратно-совместимо.
4👍1👎1💔1
Третий способ: вынос метода как значения.

Мы не можем сделать опциональный метод как таковой, но мы можем сделать ассоциированную константу типа Option — в частности, оборачивающий тип метода на Self:

type ReserveForPush<P> = fn(&mut P, usize);

trait Push<T> {
fn push_single(&mut self, x: T);
const RESERVE_FOR_PUSH: Option<ReserveForPush<Self>>;
}

impl<T> Push<T> for Vec<T> {
fn push_single(&mut self, x: T) {
self.push(x);
}
const RESERVE_FOR_PUSH: Option<ReserveForPush<Self>> =
Some(Self::reserve);
}

impl<T> Push<T> for Sender<T> {
fn push_single(&mut self, x: T) {
self.send(x).unwrap();
}
const RESERVE_FOR_PUSH: Option<ReserveForPush<Self>> = None;
}

Вызывающий код теперь может проверять в рантайме наличие метода и вызывать его:

fn push_vec<T, P>(p: &mut P, xs: Vec<T>)
where
P: Push<T>,
{
if let Some(f) = P::RESERVE_FOR_PUSH {
f(p, xs.len());
}
for x in xs {
p.push_single(x);
}
}

Из недостатков: не обратно-совместимо, нельзя статически требовать наличия метода, трейт теперь не object-safe.

===

Rust всё ещё очень слаб с обращением с константами на уровне сигнатур. Но он работает с проверками на уровне типов! Так что возможен также и

Четвёртый способ

, а именно — вынос Option на уровень типов.

Для начала определим сам type-level Option:

mod ty_opt {
// Rust не поддерживает перечисления на уровне типов,
// поэтому сделаем две альтернативы и объединим их
// через sealed trait
mod private {
pub trait Sealed {}
impl<T> Sealed for super::Some<T> {}
impl Sealed for super::None {}
}

pub struct Some<T>(pub T);
pub struct None;

pub trait TypeOption<T>: private::Sealed {
// нам нужно как-то в итоге заматчиться
// по значению, поэтому предоставим
// метод для унификации
fn unlift(self) -> Option<T>;
}

impl<T> TypeOption<T> for Some<T> {
fn unlift(self) -> Option<T> {
::core::option::Option::Some(self.0)
}
}

impl<T> TypeOption<T> for None {
fn unlift(self) -> Option<T> {
::core::option::Option::None
}
}
}

Теперь внесём этот type-level Option в интерфейс Push:

type ReserveForPush<P> = fn(&mut P, usize);

trait Push<T> {
fn push_single(&mut self, x: T);
type ReserveForPushOpt: ty_opt::TypeOption<ReserveForPush<Self>>;
const RESERVE_FOR_PUSH_OPT: Self::ReserveForPushOpt;
}

И реализуем Push<T> для всё тех же типов:

impl<T> Push<T> for Vec<T> {
fn push_single(&mut self, x: T) {
self.push(x);
}
type ReserveForPushOpt = ty_opt::Some<ReserveForPush<Self>>;
const RESERVE_FOR_PUSH_OPT: Self::ReserveForPushOpt
= ty_opt::Some(Self::reserve);
}

impl<T> Push<T> for Sender<T> {
fn push_single(&mut self, x: T) {
self.send(x).unwrap();
}
type ReserveForPushOpt = ty_opt::None;
const RESERVE_FOR_PUSH_OPT: Self::ReserveForPushOpt
= ty_opt::None;
}

Теперь мы можем сделать общую функцию, которая по возможности использует этот опциональный метод:

use ty_opt::TypeOption;

fn push_vec<T, P>(p: &mut P, xs: Vec<T>)
where
P: Push<T>,
{
if let Some(f) = P::RESERVE_FOR_PUSH_OPT.unlift() {
f(p, xs.len());
}
for x in xs {
p.push_single(x);
}
}

И у нас всё ещё есть возможность требовать наличие метода статически:

use ty_opt::TypeOption;

fn push_vec_super_duper_fast<T, P>(p: &mut P, xs: Vec<T>)
where
P: Push<T, ReserveForPushOpt = ty_opt::Some<ReserveForPush<P>>>,
{
let ty_opt::Some(f) = P::RESERVE_FOR_PUSH_OPT;
f(p, xs.len());
for x in xs {
p.push_single(x);
}
}

При желании можно даже гарантировать отсутствие метода, но мне сложно представить ситуацию, когда это было бы нужно.

Из недостатков:
* ломает обратную совместимость
* трейт не object-safe
* для общего случая нужно импортировать трейт ради метода unlift
* боже зачем вам это

Не как всегда, в этот раз без гиста.
👍7🤯1💔1
Forwarded from Протестировал (Sergey Bronnikov)
Помните SETI@home? Проект анализировал радиосигнал из космоса для поиска внеземного разума используя для этого вычислительные ресурсы добровольцев. По аналогии с SETI@Home есть проект Fuzzing@Home, в котором можно запускать фаззинг проектов, добавленных в OSS Fuzz, в обычном веб браузере. Это возможно благодаря компиляции в WebAssembly. Попробуйте сами - https://fuzzcoin.gtisc.gatech.edu:8000/
👍5💔1
DELO (18+)
17 октября 2022 года состоялись общественные слушания по принятию законопроекта, полностью запрещающего "пропаганду" ЛГБТ+. На следующую неделю запланировано первое чтение. Депутаты Госдумы высказались, что ЛГБТ – оружие Запада, угрожающее государственной…
Госдума единогласно приняла в I чтении пакет законопроектов о запрете «пропаганды нетрадиционных сексуальных отношений».

ЕДИНОГЛАСНО, БЛЯТЬ. Какие же конченые мрази.
🤬10💔5💩3😁1
Можно вытащить человека из раста, но нельзя вытащить раст из человека
🔥10🤔2💩2🙏1
Forwarded from CFLAGS="-pedantic"
👍7🔥3💩1
#prog #rust #python #article

Buffers on the edge: Python and Rust

One of my least favorite kinds of bug is when two different systems are interacting and the result has bad behavior but it’s difficult to say which (if either!) system is at fault. This is one of those stories, about Python’s buffer protocol and Rust’s memory model.
💩1
#prog #rust #performancetrap #article

Читайте документацию, программисты.

Upgradable parking_lot::RwLock might not be what you expect

Собственно, статья может быть сведена к единственному абзацу из документации parking_lot (конкретно к методу RwLock::upgradeable_read):

Locks this rwlock with upgradable read access, blocking the current thread until it can be acquired.

The calling thread will be blocked until there are no more writers or other upgradable reads which hold the lock. There may be other readers currently inside the lock when this method returns.
💩1
#prog #rust #rustlib #video

Snapshot Testing with Insta 0.16 in Rust

Да, это видео, но всего лишь 13 минут. И оно наглядно демонстрирует, почему вы можете захотеть использовать insta для своих тестов.
💩1
#prog #rust

MacroKata

Welcome to MacroKata, a set of exercises which you can use to learn how to write macros in rust. When completing each task, there are three goals:

* Get your code to compile without warnings or errors.
* Get your code to "work correctly" (i.e. produce the same output)
* Importantly, generate the same code as what the sample solution does.

You should complete the kata in order, as they increase in difficulty, and depend on previous kata.
👍9💩1
РБК Голосовалка ️
Нужно ли бороться с пропагандой ЛГБТ среди граждан всех возрастов?
М-да. А ведь в какой-то момент за последний вариант было 52% голосов
😁3💩1
Десять минут отмывала пол. Оказалось, это тень от провода падает.

А у вас как дела?))
😁9🤣21💩1