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

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

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
Forwarded from вафля 🧇🍓
единственное для чего пригоден питон — умножать строки
Forwarded from мне не нравится реальность (waffle 🧇🍓)
NerdyPepper/eva

Простой REPL калькулятор с историей и подсветкой синтаксиса. На расте.
мне не нравится реальность
Зачем вести блог, если можно просто репостить Блог*?
Зачем вести Блог*, если можно просто репостить Вафлю?
Павел Дуров поделился 7 секретами вечной молодости:

1. Наличие однопараметрического типа Т
2. Наличие аппликативного функтора для типа Т
3. Наличие функции return
4. Наличие функций join или bind
5. Закон левой идентичности
6. Закон правой идентичности
7. Закон ассоциативности
xxx: Блин, вот мне нихрена не смешно — у меня начальник тоже двинутый по теме "сделать посложнее, чтобы никто не догадался".
xxx: Пока что догадаться не можем мы.

#prog #quotes
😁1
Ёлки-палки, я вроде бекендер, почему я сейчас гуглю по работе, можно ли использовать -- внутри тегов комментариев HTML?
Научно-фантастически-исторический роман "Ложная лепота"
Какое покрытие полезнее?
Anonymous Poll
20%
Кода тестами
80%
Антипригарное
🤔1
Беды с баш #rust-ом (#prog)
Forwarded from Vlad Beskrovnyi
Из-за отсутствия внятной спецификации, в расте то и дело возникают вопросы "'это баг или фича?". И когда все-таки решат, что баг, может выясниться, что он уже всеми повсюду используется, как фича. И вроде и людей в этом не обвинишь, ведь нигде не написано, что это баг - ведь ланг дезигнеры и сами внятно сказать не могли, баг или не баг! Приходится что-то выкостыливать. Вот напр я репортил - https://github.com/rust-lang/rust/issues/74556
Тут сначала не могли понять, баг или не баг. Потом все же решили, что баг. Потом выяснили, что он уже есть в нескольких стабильных релизах. Потом исправили, но выяснили, что кто-то баг уже использует, так что на новой найтли что-то перестало компилиться. Вроде решили, что и фиг с ним
Forwarded from гиг пиг ниг
#prog #computergraphics

Статья (перевод) о попытке сделать рендеринг поверх не чисел с плавающей точкой, а поверх рациональных чисел. Спойлер: для получения приемлемых результатов автору пришлось реализовать арифметику с "плавающей дробной чертой"
Forwarded from oleg_log (Oleg Kovalov)
Collection of articles on good practices and tools to improve C code quality

Интересно тем, что от автора LZ4 и ZSTD (можно сказать лучшие компрессоры данных, да, на Си)

https://github.com/Cyan4973/Writing_Safer_C_code
#prog #rust #моё

Рассмотрим следующий код на Rust:

// код опущен для краткости

fn main() {
let tricky = <Tricky as Default>::default();
assert_eq!(tricky.field, 7);
assert_eq!(tricky.field, 4);
assert_eq!(tricky.field, 13);

let mut nice_try = <NiceTry as Default>::default();
assert_eq!(nice_try.field, 0);
nice_try.field += 12;
assert_eq!(nice_try.field, 0);

let (mut a, mut b) = <(Unstable, Unstable) as Default>::default();
assert_eq!((a.field, b.field), (0, 0));
std::mem::swap(&mut a.field, &mut b.field);
assert_eq!((a.field, b.field), (10, 10));
}


Очевидно, этот код не работает. И действительно, если мы запустим эту программу, то один из ассертов запустит панику. В данном случае это будет... Будет... Погодите-ка, оно работает? Что?! Но ведь в Rust нет свойств!

Окей, признаю, я вас немного обдурил, вся фишка кроется в коде, который я не показал. Разумеется, я его покажу, а попутно затрону две темы в Rust: deref coercion и interiour mutability.

Но сначала давайте поговорим о слайсах. Они достаточно удобны. Их можно индексировать и снова получать слайсы. Для них определено множество полезных методов: сортировка, поворот, обращения порядка, линейный и бинарный поиски... Однако, все эти методы можно также вызвать и на массивах, и на Vec. Как это работает? Неужели реализация этих методов скопирована с методов для слайсов? Конечно, нет.

Давайте посмотрим на метод Vec::sort. Этот метод описан в блоке "Methods from Deref<Target = [T]>". Что это означает? В стандартной библиотеке Rust (на самом деле core) есть трейт Deref, описанный следующим образом:

trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}

Как нетрудно видеть, он позволяет получить из ссылки на значение одного типа ссылку на значение другого типа. У этого трейта есть дополняющий его DerefMut, который выполняет преобразование между мутабельными ссылками:

trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}

Возникает закономерный вопрос: а чем они отличается от AsRef и AsMut, которые также выполняют преобразования между ссылками? Ну, во-первых, в силу того, что целевой тип преобразования у Deref и DerefMut является ассоциированным типом, а не обобщённым параметром, у любого типа может быть не более одной реализации Deref и DerefMut. А во-вторых — и это уже куда как более существенное отличие — это один из "магических" (известных компилятору) трейтов, методы которого часто вызываются неявно. Конкретно методы Deref{, Mut} вызываются для кастомных реализаций операторов разыменовывания, а также ещё неявно в некоторых контекстах, в частности, при вызове методов через точку. Это, в том числе, позволяет эргономично пользоваться умными указателями. Если на минуту забыть о том, что Vec может менять свою длину, мы можем считать его ссылкой на лежащий в куче массив с длиной, известной лишь на этапе исполнения. То есть... Указатель на слайс. И действительно, Vec реализует Deref{, Mut}<Target = [T]>, что позволяет вызывать на нём методы, определённые для слайса. И при этом без каких-либо дополнительных телодвижений с вызывающей стороны!

Ладно, это было поучительно, но какое оно имеет отношение к безобразию, показанному в начале? Дело в том, что одна из (почему-то малоизвестных) ситуаций, когда методы этих трейтов могут вызваны — это... Доступ к полю. Если в коде есть foo.field и foo имеет тип Foo, у которого нету поля field, но Foo реализует Deref<Target = Bar>, где у типа Bar есть поле field, то такое обращение к полю будет корректно и будет вызывать deref (или deref_mut). Все те странные структуры, которые я показал, так или иначе оборачивают структуру Simple:

#[derive(Default)]
struct Simple {
field: u32,
}
Давайте сначала разберём NiceTry. Вот её определение:

#[derive(Default)]
struct NiceTry {
first: Simple,
second: Simple,
}

У нас есть два поля одного типа. Как же работает трюк с подменой? Очень просто: в Deref::deref мы возвращаем ссылку на одно поле, а в DerefMut::deref_mut — на другое:

impl Deref for NiceTry {
type Target = Simple;
fn deref(&self) -> &Self::Target {
&self.first
}
}

impl DerefMut for NiceTry {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.second
}
}

Неудивительно, что мы не могли поменять field — мы не могли получить к нему мутабельный доступ!

Unstable устроен несколько сложнее. Если у нас есть мутабельный доступ к полю внутри метода, то мы можем его... Мутировать. То есть поменять. Собственно, именно это и происходит в реализации deref_mut:

#[derive(Default)]
struct Unstable(Simple);

impl Deref for Unstable {
type Target = Simple;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for Unstable {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.field += 10;
&mut self.0
}
}

"Ладно" — может сказать кто-то из моих читателей — "я понимаю, как работает трюк с NiceTry и Unstable, но что, чёрт побери, происходит с Tricky? Мы ведь даже не используем мутабельный доступ к Tricky, так что мы не можем что-то там поменять!". Что ж, мой недоумённый читатель (а также другие), я вынужден раскрыть одну из самых грязных тайн Rust: &mut T на самом деле не означает "изменяемая ссылка"! Что же это тогда? Фактически, &mut T означает уникальную ссылку на значение типа T — то есть такую, что в любой момент времени одновременно с этой ссылкой не существует никаких других. Почему это так важно? Дело, что одна из вещей, которая является в Rust неопределённым поведением — это гонка данных, ситуация, когда доступ к одному и тому же значению происходит из двух (или более) разных мест одновременно, и при этом как минимум один доступ — на запись. Простейший (концептуально, разумеется) доступ добиться отсутствия гонок данных — это убедиться, что в любой момент времени только кто-то один имеет изменяемый доступ к данным. Это именно то, что компилятор Rust проверяет, используя концепции владения и заимствования. Но это не единственный способ!

Тот же анализ заимствований, который компилятор Rust делает на этапе компиляции, можно делать во время исполнения. Именно этот паттерн и реализует RefCell. Этот тип подсчитывает количество ссылок доступов к данным во время исполнения и паникует, если в какой-то момент должны возникнуть одновременно изменяемый доступ и хоть сколько-то неизменяемых — или более одного изменяемого. На проблему гонок данных также можно взглянуть с другого конца. Гонка данных считается неопределённым поведением, поскольку для многих типов данных операция обновления не является атомарной, т. е. неделимой. Если два ядра будут менять значение одновременно, то это может привести к тому, что промежуточные операции обновления будут чередоваться друг с другом и привести к тому, что в памяти значение окажется в неожиданном состоянии — неожиданное для того, кто писал код в расчёте на атомарность изменений (на самом деле тут есть ещё более сложные противные вещи, связанные с кешами процессоров, но сегодня я не хочу в это углубляться). Однако для простых значений, умещающихся в одно машинное слово, процессор на самом деле может гарантировать атомарность изменений. Тогда, если два ядра одновременно изменяют атомарное значение, оба этих изменения будут учтены. В таком случае гонка данных просто не может причинить вред, а значит, атомарные значения вполне можно отдавать по разделяемым (&) ссылкам. И действительно, если мы откроем документацию к, скажем, AtomicU32, то мы увидим, что практически все методы принимают &self.