Rust Tip дня: матчинг слайсов.
В Rust можно элегантно извлекать элементы из слайсов с помощью паттерн-матчинга:
Извлечение с одним вариантом.
Извлечение с ранним возвратом при ошибке матчинга.
Сила pattern matching раскрывается в полной мере, когда вы:
* избегаете ручного индексирования,
* делаете распаковку данных декларативной,
* явно документируете формат входа через структуру паттерна.
Приятного вам матчинга.
В Rust можно элегантно извлекать элементы из слайсов с помощью паттерн-матчинга:
match input {
[0xFF, rest @ ..] => println!("Starts with 0xFF, then {:?}", rest),
[first, second, .., last] => println!("Start: {first:#X}, {second:#X}, end: {last:#X}"),
[.., last] => println!("Ends with {last:#X}"),
[] => println!("Empty slice"),
}
Извлечение с одним вариантом.
if let [a, b, c] = input {
println!("Exactly three elements: {a}, {b}, {c}");
}
Извлечение с ранним возвратом при ошибке матчинга.
let [first, second, rest @ ..] = input else {
panic!("Expected at least two elements");
};
println!("Parsed: {first}, {second}, rest = {:?}", rest);
Сила pattern matching раскрывается в полной мере, когда вы:
* избегаете ручного индексирования,
* делаете распаковку данных декларативной,
* явно документируете формат входа через структуру паттерна.
Приятного вам матчинга.
👍20❤2
Rust Tip для чернокнижников.
Drop-check будет считать, что
Еще одно важное отличие это авто-трейты.
Если
Следует учитывать, что
Удачной вам охоты на фантомные данные 👻
PhantomData<T>
vs PhantomData<fn() -> T>
- не одно и то жеPhantomData<T>
указывает на семантику владения T
.PhantomData<fn() -> T>
- лишь на то что где-то будет использоваться этот T
.Drop-check будет считать, что
PhantomData<T>
может вызывать Drop
у T
, а PhantomData<fn() -> T>
- не будет.Еще одно важное отличие это авто-трейты.
PhantomData<T>
наследует их от T
, а вот PhantomData<fn() -> T>
нет.Если
T
будет !Send
или !Sync
, то в первом случае и ваш тип будет. А в случае функции - нет. Указатели на функции Send
, Sync
, Unpin
и прочие Freeze
независимо от типов аргументов и возвращаемого типа.#[derive(Default)]
struct Foo<T>(PhantomData<T>);
#[derive(Default)]
struct Bar<T>(PhantomData<fn() -> T>);
let foo: Foo<Rc<u8>> = Foo::default();
let bar: Bar<Rc<u8>> = Bar::default();
std::thread::spawn(|| { drop(foo); }); // error: `Rc<u8>` cannot be sent between threads safely
std::thread::spawn(|| { drop(bar); }); // success!
Следует учитывать, что
PhantomData<fn(T)>
переворачивает ковариантность, так что его стоит использовать когда именно это и нужно.Удачной вам охоты на фантомные данные 👻
🔥17👍3❤2
Канал перевалил за 300 подписчиков!!!
🎉 Ура-ура-ура 🎉
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥24👍5🍾5❤1
Rust Tip дня:
🔹
Изолирует редкий или громоздкий код от горячего пути.
Горячий путь меньше - лучше локальность - больше производительность.
✅ Используй, если:
код вызывается редко.
хочется сократить размер inlined функций;
slow path.
🔹
Помечает функцию как исключительно редкую.
Позволяет релоцировать код для лучшей производительности горячего кода.
✅ Используй, только если:
вызов происходит <1% случаев;
код отрабатывающий только на разогреве и не используемый позже.
функция вызывается только в исключительных случаях.
⚠️ Не ставь
💡 Некоторые функции и макросы, такие как
Для
Бонус.
Если вам привычны
На stable вы можете легко их реализовать сами - просто сделайте вызов пустой холодной функции.
#[inline(never)]
и #[cold]
- когда использовать?🔹
#[inline(never)]
Изолирует редкий или громоздкий код от горячего пути.
Горячий путь меньше - лучше локальность - больше производительность.
✅ Используй, если:
код вызывается редко.
хочется сократить размер inlined функций;
slow path.
fn some_threaded_algo(rw: &RwLock<Foo>) -> Bar {
let read = rw.read();
match try_do_stuff(&*read) { // may fail with only shared ref
Ok(bar) => bar,
Err(_) => {
drop(read);
slow_path(rw)
}
}
#[inline(never)]
fn slow_path(rw: &RwLock<Foo>) -> Bar {
let mut write = rw.write();
do_stuff(&mut *write) // may not fail with exclusive ref
}
🔹
#[cold]
Помечает функцию как исключительно редкую.
Позволяет релоцировать код для лучшей производительности горячего кода.
✅ Используй, только если:
вызов происходит <1% случаев;
код отрабатывающий только на разогреве и не используемый позже.
функция вызывается только в исключительных случаях.
⚠️ Не ставь
#[cold]
, если функция вызывается даже изредка - даже 1–2% уже много.💡 Некоторые функции и макросы, такие как
panic!()
, сами по себе уже #[cold]
, дополнительная аннотация не требуется.Для
slow_path
из примера выше можно добавить #[cold]
, если необходимость в экслюзивном локе это исключительная редкость.Бонус.
Если вам привычны
likely
и unlikely
, что бы помечать ветки как холодные, они есть в nightly.На stable вы можете легко их реализовать сами - просто сделайте вызов пустой холодной функции.
#[inline(always)]
fn likely(b: bool) -> bool {
if b {
true
} else {
cold_path();
false
}
}
#[inline(always)]
fn unlikely(b: bool) -> bool {
if b {
cold_path();
true
} else {
false
}
}
#[inline(always)]
#[cold]
fn cold_path() {}
❤5👍2😱1💯1
Rust Tip мини
Остерегайтесь расставлять #[inline(always)].
#[inline] достаточно для того что бы компилятор заинлайнил функцию, если сочтет нужным (без него кросс библиотечные вызовы могут не инлайниться).
#[inline(always)] может привести к тому что код слишком разбухнет и нарушится локальность.
Дайте оптимизатору выполнять его работу.
Остерегайтесь расставлять #[inline(always)].
#[inline] достаточно для того что бы компилятор заинлайнил функцию, если сочтет нужным (без него кросс библиотечные вызовы могут не инлайниться).
#[inline(always)] может привести к тому что код слишком разбухнет и нарушится локальность.
Дайте оптимизатору выполнять его работу.
🤝10💯1
Нужна помощь зала.
Не могу придумать, как изобразить плоскости в визуализаторе к athena.
(Это там где точки и линии).
2д хорошо работает, надо 3д доработать.
И вот там и появляются плоскости.
Но они ж бесконечные. И в проекции на экран станут либо линией, либо весь вьюпорт займут.
Если изображать какой-то прямоугольник на плоскости, то какой?
Не могу придумать, как изобразить плоскости в визуализаторе к athena.
(Это там где точки и линии).
2д хорошо работает, надо 3д доработать.
И вот там и появляются плоскости.
Но они ж бесконечные. И в проекции на экран станут либо линией, либо весь вьюпорт займут.
Если изображать какой-то прямоугольник на плоскости, то какой?
Представьте, что вам сейчас надо выбрать графический API для нового проекта.
Выбрали ли бы вы
Если да, то почему? Если нет, то почему?
Если не слышали раньше, то вот, посмотрите пожалуйста 🥺
https://github.com/zakarumych/mev
Выбрали ли бы вы
mev
?Если да, то почему? Если нет, то почему?
Если не слышали раньше, то вот, посмотрите пожалуйста 🥺
https://github.com/zakarumych/mev
GitHub
GitHub - zakarumych/mev: GAPI
GAPI. Contribute to zakarumych/mev development by creating an account on GitHub.
😁3🔥1
Rust Tip для начинающих
Реализуя
К сожалению трейты из метода
Но можно еще посмотреть на то что бы перегрузить
Удачного вам итерирования!
Реализуя
Iterator
, снабдите его кастомной реализацией size_hint
, nth
, count
, last
и fold, если они могут быть лучше чем реализованный в трейте.size_hint
по-умолчанию возвращает (0, None), что значит от нуля до бесконечности. Его можно перегрузить почти всегда.nth
по-умолчанию просто вызывает next
n+1 раз. Если итератор может прыгнуть - кастомная реализация будет кстати.count
по-умолчанию итерируется до конца и подсчитывает. Если размер известен заранее, то стоит перегрузить count
. А еще реализовать ExactSizedIterator
Если размер известен и доступ к любому элементу есть, то перегружайте last
, что бы этот метод не итерировался от начала и до концаfold
по-умолчанию вызывает в цикле next
. Если в next
есть какая-то дополнительная логика, которая не нужна если итератор поглощается целиком, то кастомный fold
поможет избавиться лишних вычислений.К сожалению трейты из метода
try_fold
еще недоступны на stable, так что его перегрузить не получится.Но можно еще посмотреть на то что бы перегрузить
all
и any
.Удачного вам итерирования!
👍25
Rust Tip выходного дня.
Rust это крутой язык, но отдыхать очень важно.
Лучше не тратить на петы все выходные.
Постарайтесь выйти на прогулку.
После выгорания программировать на Rust становится сложнее.
Rust это крутой язык, но отдыхать очень важно.
Лучше не тратить на петы все выходные.
Постарайтесь выйти на прогулку.
После выгорания программировать на Rust становится сложнее.
🤝20🔥6❤3👎3👍2💯2
Известно, что файлы - самураи. Потому что у файла нет цели, только путь.
😁15🤯9🔥3⚡1
Шутки, которые нельзя перевести лучше не переводить.
-
- Assuming multiplications in
-
this^2 = -1
. Is this real?- Assuming multiplications in
t*h*i*s
are commutative, than it's equivalent is shit, and shit is getting real!👍14🔥1
Любой ieee754 формат с плавающей точкой содержит:
0% натуральных чисел.
Столько же отрицательных целых.
200% нулей.
И 0% остальных рациональных чисел.
0% натуральных чисел.
Столько же отрицательных целых.
200% нулей.
И 0% остальных рациональных чисел.
😁14👍2
Сорвалась сессия в ДнД.
Второй раз подряд.
Вывод: нельзя лить в прод в пятницу. Инциденты на работе мешают спасать мир.
Второй раз подряд.
Вывод: нельзя лить в прод в пятницу. Инциденты на работе мешают спасать мир.
😁10💯4
Rust Tip для тех, кто кодит ночью (или прочитает утром)
Давайте разберем семейство трейтов
Это семейство представляет собой иерархию
Что же они означают? На самом деле все очень просто, стоит лишь разложить все по полочкам.
Семейство Fn использует специальный синтаксический сахар, и мы обязаны писать так:
Вместо
Далее если не указаны аргументы и возвращаемый тип, значит информация применима к любым аргументам и типам возврата.
Итак по порядку. 🧵
Давайте разберем семейство трейтов
Fn
.Это семейство представляет собой иерархию
FnOnce
, FnMut
, Fn
, где следующий включает предыдущий.Что же они означают? На самом деле все очень просто, стоит лишь разложить все по полочкам.
Семейство Fn использует специальный синтаксический сахар, и мы обязаны писать так:
Fn(A, B, C) -> R
, аналогично синтаксису функций, только без имен аргументов.Вместо
Fn<Args, Output=R>
, где Args
- тьюпл с типами аргументов, так как эта форма нестабильна.Далее если не указаны аргументы и возвращаемый тип, значит информация применима к любым аргументам и типам возврата.
Итак по порядку. 🧵
👍10🤝1
Понедельничный Rust Tip.
Простой совет для невыспавшихся за выходные.
Старайтесь не добавлять трейт баунды в объявление типа.
Любой
В результате некоторые методы, которые никак не зависят это реализации этого трейта, будут на него завязаны.
Конечно иногда придется. Например если вы используете ассоциированный тип в объявлении полей.
Простой совет для невыспавшихся за выходные.
Старайтесь не добавлять трейт баунды в объявление типа.
Любой
T: Trait
потребуют добавить этот баунд во все имплы и функции, кроме случаев, где вместо T
будет конкретный тип.В результате некоторые методы, которые никак не зависят это реализации этого трейта, будут на него завязаны.
Конечно иногда придется. Например если вы используете ассоциированный тип в объявлении полей.
👍6
Запоздалый Rust Tip.
Используйте impl AsRef<T>, impl AsMut<T> и impl Into<T> в аргументах, когда ожидается, что вызываться функция будет удобнее с автоматической конверсией без потери выразительности API.
Но только если
Классический пример это AsRef<Path> аргументы в std всюду, где нужен &Path, потому что удобно вызывать со строковым литералом. При этом на вызывающей стороны не возникает вопросов, куда там оно сконвертируется.
Еще хороший пример - impl Into<EnumType>, где для вариантов есть отдельный тип, который конвертируется в этот вариант. Концептуально они должны быть одним и тем же.
Использовать ли impl Trait или объявить generic, дело вкуса по большей части. Единственная разница в невозможности явно указать тип. Если вы ожидаете, что пользователю функции когда-то понадобится явно указать тип - объявляйте, иначе можно и с анонимным.
Используйте impl AsRef<T>, impl AsMut<T> и impl Into<T> в аргументах, когда ожидается, что вызываться функция будет удобнее с автоматической конверсией без потери выразительности API.
Но только если
T
это не дженерик. Иначе придется его явно указывать.Классический пример это AsRef<Path> аргументы в std всюду, где нужен &Path, потому что удобно вызывать со строковым литералом. При этом на вызывающей стороны не возникает вопросов, куда там оно сконвертируется.
Еще хороший пример - impl Into<EnumType>, где для вариантов есть отдельный тип, который конвертируется в этот вариант. Концептуально они должны быть одним и тем же.
Использовать ли impl Trait или объявить generic, дело вкуса по большей части. Единственная разница в невозможности явно указать тип. Если вы ожидаете, что пользователю функции когда-то понадобится явно указать тип - объявляйте, иначе можно и с анонимным.
👍6👏1💯1
Вот еще один Rust Tip
Делайте derive макросы для своих трейтов.
Если из структуры типа примерно понятно, как реализовать трейт - этот трейт отличный кандидат для
Это значительно уменьшит количество бойлерплейта и уменьшит вероятность глупых ошибок, что мы можем допустить при ручной реализации трейта.
Процедурные макросы могут показаться довольно сложными сначала. Надо парсить и генерировать код.
На самом деле все просто, так как у нас есть крейты
С помощью
А
Не забудьте при генерации Rust кода устанавливать корректные
Так же существует множество надстроек, что делают процедурные макросы вовсе тривиальными. Хотя каждая обладает своими ограничениями.
Делайте derive макросы для своих трейтов.
Если из структуры типа примерно понятно, как реализовать трейт - этот трейт отличный кандидат для
derive
.Это значительно уменьшит количество бойлерплейта и уменьшит вероятность глупых ошибок, что мы можем допустить при ручной реализации трейта.
Процедурные макросы могут показаться довольно сложными сначала. Надо парсить и генерировать код.
На самом деле все просто, так как у нас есть крейты
syn
и quote
.С помощью
syn
можно без труда распарсить код на Rust, который передается в процедурный макрос. А с помощью иных крейтов можно и содержимое аттрибутов распарсить как по волшебству.А
quote
поможет сгенерировать код реализации трейта из шаблона с подстановками.Не забудьте при генерации Rust кода устанавливать корректные
Span
-ы. Это поможет пользователю макроса найти причину ошибки.Так же существует множество надстроек, что делают процедурные макросы вовсе тривиальными. Хотя каждая обладает своими ограничениями.
❤10👍6
Пятнично.
Я заметил, что у меня есть почти все ачивки в ER.
И не хватало только пары легендарок и 2х концовок.
И у меня был перс, прошедший базу в Jorney 2, кроме ластов.
Что, ж. Собрал леги (пришлось пройти весь квест Ранни, но на это ушел всего час), завалил ластов.
Бегом в Jorney 3. Взял 2 великие руны (Годрик и Райкард), проскочил Линдел, Напрямик к Огненному Гиганту, заваливая только боссов, что встречались по дороге.
Фарум Азула как нож сквозь масло (Jorney 3 кажется проще чем Jorney 2).
Маликет взят с первого пула, несмотря на небольшой факап с бафанием.
На Всезнайке попотел.
Годфри дал жару еще хлеще, оставил его на утро. Утром с первого пула.
Радагон и Зверь с нескольких пулов.
Я точно мучался на Jorney 2 намного больше.
А ведь за весь Jorney 3 я вкачал че-то типа 240-245. Потому что руны с нескольких боссов профукал на вайпах на следующих.
В целом что хочу сказать.
Elden Ring это одна из лучших игр, в какие я играл. Максимально советую всем любителям помахать мечем.
И не бойтесь сложности, она проще чем Dark Souls.
Я заметил, что у меня есть почти все ачивки в ER.
И не хватало только пары легендарок и 2х концовок.
И у меня был перс, прошедший базу в Jorney 2, кроме ластов.
Что, ж. Собрал леги (пришлось пройти весь квест Ранни, но на это ушел всего час), завалил ластов.
Бегом в Jorney 3. Взял 2 великие руны (Годрик и Райкард), проскочил Линдел, Напрямик к Огненному Гиганту, заваливая только боссов, что встречались по дороге.
Фарум Азула как нож сквозь масло (Jorney 3 кажется проще чем Jorney 2).
Маликет взят с первого пула, несмотря на небольшой факап с бафанием.
На Всезнайке попотел.
Годфри дал жару еще хлеще, оставил его на утро. Утром с первого пула.
Радагон и Зверь с нескольких пулов.
Я точно мучался на Jorney 2 намного больше.
А ведь за весь Jorney 3 я вкачал че-то типа 240-245. Потому что руны с нескольких боссов профукал на вайпах на следующих.
В целом что хочу сказать.
Elden Ring это одна из лучших игр, в какие я играл. Максимально советую всем любителям помахать мечем.
И не бойтесь сложности, она проще чем Dark Souls.
👍5👎4🤷♂2