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

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

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
Но что же используется внутри Tricky? Какое-то атомарное значение? Нет, field — это просто u32, а сделать атомарной саму структуру Simple мы не можем. RefCell? Тоже нет. Метод RefCell::borrow возвращает значение особого типа, которое реализует Deref в оборачиваемые данные и которое в деструкторе уменьшает счётчик неизменяемых доступов. Но, напомним, в методе deref нам нужно вернуть именно ссылку. Единственный способ получить получить ссылку на внутреннее значение в RefCell — это вызвать borrow{, _mut}, который вернёт тот прокси-тип. Но Rust просто не даст нам вернуть ссылку на локальную переменную! Выходит, этот вариант тоже отпадает. Что же тогда используется в Tricky?

Ответ — Cell! Этот тип оборачивает значение и позволяет менять их по разделяемой ссылке. Почему же это не может привести к гонкам данных? На это есть две причины. Первая — API у Cell позволяет доставать (копию) внутреннего значения, переписывать значение, замещать его другим (в том числе и значением по умолчанию) и обмениваться значением с другим Cell, но не даёт возможности получить разделяемую ссылку на значение внутри — это дало бы возможность передать ссылку в другой поток и организовать чтение из одного потока и запись в другом (кстати, достать &mut ссылку можно, ибо её уникальность гарантирует компилятор). Вторая причина — и это, на мой взгляд, очень хорошо демонстрирует силу Rust — Cell не реализует Sync, то есть &Cell<T> нельзя пересылать из одного потока в другой. Это означает, что в любой момент времени ссылки на Cell, если таковые есть, все принадлежат одному потоку, то есть коду, который по отношению к Cell исполняется последовательно. Таким образом, даже не смотря на то, что доступ к Cell может быть из нескольких мест и каждый доступ может менять значение внутри, однопоточность кода гарантирует, что эти доступы не могут быть активны одновременно.

Вооружившись этим знанием, нетрудно понять, как реализована Tricky:

struct Tricky {
choice: Cell<Choice>,
first: Simple,
second: Simple,
third: Simple,
}

impl Deref for Tricky {
type Target = Simple;
fn deref(&self) -> &Self::Target {
let choice = self.choice.get();
self.choice.set(choice.next());
match choice {
Choice::First => &self.first,
Choice::Second => &self.second,
Choice::Third => &self.third,
}
}
}

Здесь Choice — это некоторое перечисление, которое являет собой выбор из трёх вариантов. В deref код обновляет выбор поля и в соответствии с предыдущим значением choice выбирает нужное поле.

Обычно значение в Rust можно поменять в том случае, если код им владеет или имеет &mut-ссылку на него, но нельзя через &-ссылку. Это называется inherited mutability (наследуемая изменяемость), поскольку значение "наследует" изменяемость обёртки. Однако, как мы видели, значение может быть изменяемым и через &-ссылку. Это называется interiour mutability (внутренняя изменяемость). Это нужная на практике вещь, поскольку Rc/Arc и мьютекс с безопасным интерфейсом без неё создать невозможно.

Тут у читателя может возникнуть вопрос: имеет ли смысл использовать подобные трюки у себя в коде? Скорее всего, да, если вы реализуете свой умный указатель (правда, в таком случае у меня уже к вам будут вопросы). Для всего остального? Да пожалуйста. Только будьте готовы к тому, что коллеги больше не захотят с вами разговаривать.

PS: код.
Дальнейшее чтение:

- UnsafeCell — единственный тип, который можно легально (но, конечно, небезопасно) менять по разделяемой ссылке. Абсолютно все типы с interiour mutability в конечном счёте опираются именно на него.
- Focusing on ownership — старая (до выхода Rust 1.0) статья Никитоса Niko Matsakis, в которой он предлагает заменить &mut на &uniq, убедительно аргументируя, что это куда точнее отражает суть. Это предложение вызвало в своё время бурные обсуждения, но в итоге, как вы видите, так и не было принято. Возможно, и зря.
- Common Rust Lifetime Misconceptions (мой перевод) — вскользь касается темы уникальности ссылок в пункте 9.
- Deadlock empire — офигенная образовательная игра, в которой нужно взять на себя роль планировщика потоков и, манипулируя порядком исполнения шагов, сломать программу (завести в дедлок, заставить два потока одновременно зайти в критическую секцию и т. д.). Наглядно показывает, насколько сильно в многопотоке мешается неатомарность изменений. Если честно, даже странно, что я раньше её не выкладывал.
- Rustonomicon, конкретно раздел про параллельное исполнение. nuff said.
- Readonly — хтонический макрос от Толяна, который позволяет сделать помеченные поля публичными, но при этом неизменяемыми (извне крейта). Также абьюзит Deref.
#prog #article #amazingopensource

Алгоритм валидации UTF-8 практически без условных переходов, использующий меньше одной процессорной инструкции на байт входных данных. Реализация доступна как часть библиотеки simdjson
Forwarded from <илья as Человек> (ilya sheprut)
кстати, а ведь vi/vim хорошо звучит как нейтральная версия на замену множественному числу they/them
#prog #rust

Хозяйке на заметку

Пара советов по строкам в Rust:

1) Если вам нужно разбить строку по одному из нескольких возможных символов — не спешите расчехлять регулярки, для это задачи вполне хватит стандартной библиотеки. Множество строковых методов навроде {, r}split{, _terminator}, trim{, _start, _end}_matches, find и прочие принимают в качестве аргумента для поиска значение, тип которого реализует пока нестабильный трейт Pattern. В настоящий момент его реализуют &str, &&str, &String, impl FnMut(char) -> bool и (почему-то малоизвестный) &[char]. Таким образом, разбить строку по нескольким символам легко:

let result = "Hello, world!".split(&['o', 'l'][..]).collect::<Vec<_>>();
assert_eq!(result, vec!["He", "", "", ", w", "r", "d!"]);

2) Если функция принимает на вход &mut std::str::Chars, что она может с ним сделать?

Вызвать `next`?

Нет.

Также вызвать `next_back`?

Нет.

Это всё неполные ответы. Если мы получаем мутабельную ссылку на Chars, мы можем редактировать произвольным образом, в том числе и поменять его целиком. Chars внутри себя содержит строки, символы которой он перебирает, и при помощи метода Chars::as_str эту строку можно достать. Таким образом, имея мутабельную ссылку на Chars, можно вытащить из него строку, вырезать из строки нужный кусок и переписать переданный итератор .chars() от этого кусочка.

Покажу на примере.

Вот первый способ вытащить строку из Chars (медленный, требующий аллокаций и не совсем корректный):

fn extract_line2(chars: &mut Chars) -> String {
chars.take_while(|&ch| !matches!(ch, '\r' | '\n')).collect()
}

Второй способ (побыстрее и не требующий аллокаций):

fn extract_line<'s>(chars: &mut Chars<'s>) -> Option<&'s str> {
let s = chars.as_str();
let line = s.lines().next()?;
*chars = s[line.len()..].chars();
Some(line)
}
#prog #article

Статья об антипаттерне использования File.exists.

да, это статья десятилетней давности, ну и что?
#prog #article

Статья о Cyclone и его влиянии на другие языки программирования. Ни разу не полная, а скорее обзорная.

Есть перевод, но он мне показался малость кривым.
Блог*
Донаты! Донаты?
Я, конечно, совсем ни на что не намекаю, но вот вам номер моей карты:
4274 3200 5402 8520
Блог*
Я, конечно, совсем ни на что не намекаю, но вот вам номер моей карты: 4274 3200 5402 8520
Не переживайте, я не собираюсь возводить paywall, все посты в обозримом будущем продолжат публиковаться на канале
Forwarded from мне не нравится реальность (waffle 🧇🍓)
Хвастаюсь: мой PR добавляющий as_str методы к разным Split-строковым итераторам недавно смерджил толян!

[Tracking Issue]
Я ведь уже говорил, что Вафель молодец?
Как же достали люди, которые носят маски, но при этом не прикрывают нос. Они что, правда не понимают, что это всё равно что без маски ходить?
В Rust нельзя взять срез от слайса в константном выражении, АААААААААААА
Да, я действительно это реализовал. И напишу, как (в отличие от одного рельефного печенья)
#prog #zig #article

Статья о том, как можно использовать zig cc для простой кросс-компиляции программ на C. Вместе с подробностями о реализации.