Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
202 - Telegram Web
Telegram Web
Свежий Rust Tip по свежему stable API

С версии 1.86 в stable стал доступен метод slice::get_disjoint_mut.
Который позволяет получить мутабельные ссылки на разные элементы слайса одновременно.

Разность проверяется методом и при пересечении возвращается ошибка.
Равно как и при индексе вне диапазона.

Вот как его можно использовать

let mut array = [10, 20, 30, 40, 50];

let [a, b, c] = array.get_disjoint_mut([1, 3, 4]).expect("Index overlap");
*a += 100; // 20 -> 120
*b += 100; // 40 -> 140
*c += 100; // 50 -> 150


Так же как get, можно использовать Range, RangeInclusive, RangeFrom, RangeTo и RangeFull, хотя все кроме первых двух для этого метода вряд ли пригодятся.
Так как в метод передается массив, то все должны быть одного типа.


let [a, b, c] = array.get_disjoint_mut([0..2, 2..3, 3..5]).expect("Range overlap");
a.copy_from_slice(&[9, 10]);
b.copy_from_slice(&[11]);
c.copy_from_slice(&[12, 13]);


Похожим методом обзавелся и HashMap, но там вместо возвращения ошибки метод паникует, и возвращает массив Option.

let mut map = HashMap::from([
("a", 1),
("b", 2),
("c", 3),
]);

let [oa, ob] = map.get_disjoint_mut(["a", "b"]);

if let (Some(a), Some(b)) = (oa, ob) {
*a += 10;
*b += 20;
}



Для любителей unsafe есть версии *_unchecked этих методов, которые пропускают проверку непересечения индекстов/диапазонов/ключей.

Удачного вам мулти-индексирования.
Придумывал глупую шутку про программирование.
Варианты:

1. Добавил обработку краевых условий. Теперь не работает на нормальных.
2. Поймал исключение. До сих пор держу.
3. Поднял прод. Теперь он надо мной.
4. Вызвал функцию. Она не пришла.
5. Написал универсальную функцию. Не подошла ни к одному случаю.
6. Придумал абстракцию. Теперь боюсь к ней прикасаться.
7. Вынес повторяющийся код. Повторяется в другом месте.
8. Написал комментарий. Теперь только он и понятен.
Rust Tip для тех кто отлаживает UB в своем unsafe коде в 2 часа ночи.

Делайте маленькие unsafe блоки. Стремитесь к одному выражению.
Всегда сопроводите комментарием, почему это safe.

Используйте линт, что б заставить себя писать unsafe блоки в unsafe функциях, если вы на старой версии компилятора.

Не пишите unsafe, если можете обойтись.
This media is not supported in your browser
VIEW IN TELEGRAM
Добрался сделать умножение моторов на скаляр. Что позволяет интерполировать и экстраполировать.
Rust Tip дня: матчинг слайсов.

В 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 раскрывается в полной мере, когда вы:

* избегаете ручного индексирования,
* делаете распаковку данных декларативной,
* явно документируете формат входа через структуру паттерна.


Приятного вам матчинга.
Rust Tip для чернокнижников.

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)> переворачивает ковариантность, так что его стоит использовать когда именно это и нужно.

Удачной вам охоты на фантомные данные 👻
Канал перевалил за 300 подписчиков!!!

🎉 Ура-ура-ура 🎉
Please open Telegram to view this post
VIEW IN TELEGRAM
Rust Tip дня: #[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() {}
Rust Tip мини

Остерегайтесь расставлять #[inline(always)].

#[inline] достаточно для того что бы компилятор заинлайнил функцию, если сочтет нужным (без него кросс библиотечные вызовы могут не инлайниться).

#[inline(always)] может привести к тому что код слишком разбухнет и нарушится локальность.

Дайте оптимизатору выполнять его работу.
Нужна помощь зала.

Не могу придумать, как изобразить плоскости в визуализаторе к athena.
(Это там где точки и линии).

2д хорошо работает, надо 3д доработать.
И вот там и появляются плоскости.

Но они ж бесконечные. И в проекции на экран станут либо линией, либо весь вьюпорт займут.

Если изображать какой-то прямоугольник на плоскости, то какой?
Представьте, что вам сейчас надо выбрать графический API для нового проекта.
Выбрали ли бы вы mev?
Если да, то почему? Если нет, то почему?

Если не слышали раньше, то вот, посмотрите пожалуйста 🥺
https://github.com/zakarumych/mev
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.

Удачного вам итерирования!
Rust Tip выходного дня.

Rust это крутой язык, но отдыхать очень важно.
Лучше не тратить на петы все выходные.
Постарайтесь выйти на прогулку.
После выгорания программировать на Rust становится сложнее.
Известно, что файлы - самураи. Потому что у файла нет цели, только путь.
Шутки, которые нельзя перевести лучше не переводить.

- 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!
Любой ieee754 формат с плавающей точкой содержит:
0% натуральных чисел.
Столько же отрицательных целых.
200% нулей.
И 0% остальных рациональных чисел.
Сорвалась сессия в ДнД.
Второй раз подряд.
Вывод: нельзя лить в прод в пятницу. Инциденты на работе мешают спасать мир.
Rust Tip для тех, кто кодит ночью (или прочитает утром)

Давайте разберем семейство трейтов Fn.
Это семейство представляет собой иерархию FnOnce, FnMut, Fn, где следующий включает предыдущий.
Что же они означают? На самом деле все очень просто, стоит лишь разложить все по полочкам.

Семейство Fn использует специальный синтаксический сахар, и мы обязаны писать так:
Fn(A, B, C) -> R, аналогично синтаксису функций, только без имен аргументов.
Вместо Fn<Args, Output=R>, где Args - тьюпл с типами аргументов, так как эта форма нестабильна.

Далее если не указаны аргументы и возвращаемый тип, значит информация применима к любым аргументам и типам возврата.

Итак по порядку. 🧵
2025/07/07 14:25:50
Back to Top
HTML Embed Code: