
Библиотека, которая возвращает
Cow
вместо String
для трансформированных строк, если это возможно. Всегда знал, что кто-то напишет нечто подобное, на мой взгляд, очень полезная вещь.https://github.com/RReverser/cow-utils-rs
Size: a a a
Cow
вместо String
для трансформированных строк, если это возможно. Всегда знал, что кто-то напишет нечто подобное, на мой взгляд, очень полезная вещь.Cow
вместо String
для трансформированных строк, если это возможно. Всегда знал, что кто-то напишет нечто подобное, на мой взгляд, очень полезная вещь.A SmolStr is a string type that has the following properties:https://github.com/rust-analyzer/smol_str
* size_of::<SmolStr>() == size_of::<String>()
* Clone is O(1)
* Strings are stack-allocated if they are:
* Up to 22 bytes long
* Longer than 22 bytes, but substrings of WS (see src/lib.rs). Such strings consist solely of consecutive newlines, followed by consecutive spaces
* If a string does not satisfy the aforementioned conditions, it is heap-allocated
Unlike String, however, SmolStr is immutable. The primary use case for SmolStr is a good enough default storage for tokens of typical programming languages. Strings consisting of a series of newlines, followed by a series of whitespace are a typical pattern in computer programs because of indentation. Note that a specialized interner might be a better solution for some use cases.
poll
, пока он не вернёт значение. Выполнение этой задачи возложено на экзекутор: программный компонент, который продвигает прогресс футур и, возможно, переключается между ними. 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 абстракций!I
и bool
Fuse
хранит Option<I>
, который выставляется в None
, когда нижележащий итератор возвращает None
. Для fused итераторов этот Option
никогда не выставляется в None
, а в ветке, в которой разбираются варианты, для None
используется intrinsics::unreachable. Ради изоляции unsafe
код ещё и был вынесен в отдельный файл. Этот PR был в итоге смержен (несмотря на очевидное изменение поведения в плане дропа нижележащего итератора), поэтому мне свой PR было проще выкинуть, чем пытаться адаптировать, что я и сделал.Fuse
теперь хранится в Result<I, StopStateOf<I>>
, а смена состояния Fuse
делегируется этому ассоциированному типу. Для обычных итераторов это ()
, и его метод выставляет Result
в Err(())
, а для fused итераторов это Infallible
, и его метод ничего не делает. Таким образом, мы убиваем двух зайцев одним выстрелом: мы убираем ветвление по тегу без unsafe
и не храним тег для fused итератора (не говоря уже о том, что мы убираем кучу специализированного кода). Но в бочке мёда нашлась ложка урана: схожий PR 33090, который добавлял специализации для iter::Zip
и, в частности, менял поля в зависимости от итераторов был частично откатан в PR 36490 из-за слома обратной совместимости: после этого PR Zip
, который раньше был ковариантным, становился инвариантным. Та же проблема не минула и мой PR: нижеприведённый код компилируется до моего PR и не компилируется после:fn test_fuse_covariant<'a, I>(iter: Fuse<&'static I>) -> Fuse<&'a I> {
iter
}
Fuse
, в котором лежит долгоживущая ссылка, то мы должны быть в состоянии использовать его там, где ожидается Fuse
с ссылкой, живущей меньше. После обновления же код ломается. В принципе, это логично: для компилятора в обобщённом контексте ассоциированный тип непрозрачен, и он не может заранее сказать, какая у него корректная вариантность, поэтому компилятор избирает консервативный подход и предполагает, что ассоциированный тип инвариантен. Что не является логичным, так это то, что добавления ограничения 'static
, которое явно говорит о том, что ассоциированный тип не содержит нестатических ссылок, не помогает. Да, это действительно недочёт в компиляторе, но, как мне сказал в дискорде @eddyb, инвариантность важна для обеспечения корректности кода. Ну и, разумеется, способов указать вариантность ассоциированного типа нет ¯\_(ツ)_/¯