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

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

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
Посмотрим, как реализован Iterator::next для Fuse:
    default fn next(&mut self) -> Option<<I as Iterator>::Item> {
if self.done {
None
} else {
let next = self.iter.next();
self.done = next.is_none();
next
}
}
Здесь нагло эксплуатируется тот факт, что поле done имеет булев тип. Если мы хотим, чтобы адаптер не содержал флаг для fused итератора, нам нужно сделать тип флага зависящим от типа итератора. Т. к. теперь мы не можем считать флаг булевым, нам нужно абстрагироваться от конкретного типа. Немного перепишем реализацию метода:
    default fn next(&mut self) -> Option<<I as Iterator>::Item> {
if self.done {
None
} else {
let next = self.iter.next();
if next.is_none() {
self.done = true;
}
next
}
}
Как видно, от флага требуется две операции: проверка на то, выставлен ли он, и его установка в положение "да, сэр, к сожалению, этот джентельмен вернул None". Также нам нужно каким-то образом получить начальное значение флага, когда мы создаём адаптер. Выразим это в трейте:
trait Flag: Default {
fn is_set(&self) -> bool;
fn set(&mut self);
}
Этот трейт тривиально реализуется для bool:
impl Flag for bool {
fn is_set(&self) -> bool {
*self
}

fn set(&mut self) {
*self = true
}
}
Теперь подумаем, что нам требуется для fused итератора. Наш метод next будет эквивалентен вызову next у нижележащего итератора, если флаг всё время ведёт себя так, как будто он не выставлен (рекомендую ещё раз посмотреть на код выше, чтобы убедиться в этом). Создадим соответствующий тип и реализуем для него Flag:
#[derive(Default)]
struct False;

impl Flag for False {
fn is_set(&self) -> bool {
false
}

fn set(&mut self) {}
}
Теперь нам нужно сопоставить каждому итератору соответствующий тип флага. Именно здесь нам понадобится специализация: по умолчанию для итератора флагом является булев тип, но для fused итераторов (т. е. реализующих FusedIterator) это будет False. Непосредственно функций на уровне типов в Rust нет, но их роль играют трейты с ассоциированными типами.
trait FlagType: Iterator {
type Flag: Flag + Default;
}

impl<I: Iterator> FlagType for I {
// ключевое слово default позволяет нам переопределять элементы трейтов
// в более специфичных impl-ах...
default type Flag = bool;
}

impl<I: FusedIterator> FlagType for I {
// ...что мы и делаем
type Flag = False;
}
Теперь напишем сам адаптер. Ничего сложного в нём нет, нужно только иметь в виду, что нам требуется тип, для которого определён флаг:
struct SlimFuse<I: FlagType> {
iter: I,
finished: I::Flag,
}
После всего описанного выше нетрудно написать реализацию Iterator:
impl<I: FlagType> Iterator for SlimFuse<I> {
type Item = I::Item;

fn next(&mut self) -> Option<Self::Item> {
if self.finished.is_set() {
return None;
}

let ret = self.iter.next();
if ret.is_none() {
self.finished.set();
}
ret
}
}
Осталось только приправить extension trait для того, чтобы адаптер было удобно создавать:
trait IteratorExt: Sized + FlagType {
fn fuse_slim(self) -> SlimFuse<Self>;
}

impl<I: Sized + FlagType> IteratorExt for I {
fn fuse_slim(self) -> SlimFuse<Self> {
SlimFuse {
iter: self,
finished: <_>::default(),
}
}
}
Теперь проверим, как это всё работает:
use std::mem::size_of_val;
use std::iter;

// FromFn не реализует FusedIterator по понятным причинам
let it1 = iter::from_fn(|| None::<i32>).fuse();
let it2 = iter::from_fn(|| None::<i32>).fuse_slim();

// В этом случае SlimFuse не лучше (но и не хуже!), чем Fuse
assert_eq!(size_of_val(&it1), size_of_val(&it2));

// Итераторы возвращают одно и то же
assert!(it1.eq(it2));

// Теперь возьмём fused итератор. Все итераторы коллекций являются fused —
// в частности, std::slice::Iter.
let it1 = [1, 2, 3].iter().fuse();
let mut it2 = [1, 2, 3].iter().fuse_slim();

// Наш адаптер эксплуатирует свойства итератора и оказывается менее жирным!
assert!(size_of_val(&it1) > size_of_val(&it2));

// Проверим, что наш итератор выдаёт то же самое, что и стандартный
assert!(it1.eq(it2.by_ref()));

// Проверим, что с наш адаптер действительно fused
assert!(it2.by_ref().eq(iter::empty::<&i32>()));
assert!(it2.by_ref().eq(iter::empty::<&i32>()));

Замечательно. Торжество zero-cost абстракций!

Весь код в этих постах я выложил гистом на GitHub
Forwarded from Aikidos
Блог*
О, число подписчиков перевалило за сотню! Спасибо
Уже 150 подписчиков! 🎉 Хорошая скорость, однако
This media is not supported in your browser
VIEW IN TELEGRAM
Блог* pinned «Уже 150 подписчиков! 🎉 Хорошая скорость, однако»
Forwarded from You Had No Job
Me: can we have some Java?
Mom: we have Java at 3 bn devices
Java on 3 bn devices:
Блог* pinned «Ради чего вы подписаны на канал?»
Как бы и #meme, но держать в голове всё же стоит
Forwarded from шило на мыло
Forwarded from The After Times
Тяжёлые времена настали. Последний хэш без соли вычисляем
Извините за шитпостинг, возможно, сегодня свой пост выложу.
Лучшие стикеры
This media is not supported in your browser
VIEW IN TELEGRAM