Но что же используется внутри
Ответ — Cell! Этот тип оборачивает значение и позволяет менять их по разделяемой ссылке. Почему же это не может привести к гонкам данных? На это есть две причины. Первая — API у
Вооружившись этим знанием, нетрудно понять, как реализована
Обычно значение в Rust можно поменять в том случае, если код им владеет или имеет
Тут у читателя может возникнуть вопрос: имеет ли смысл использовать подобные трюки у себя в коде? Скорее всего, да, если вы реализуете свой умный указатель (правда, в таком случае у меня уже к вам будут вопросы). Для всего остального? Да пожалуйста. Только будьте готовы к тому, что коллеги больше не захотят с вами разговаривать.
PS: код.
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: код.
doc.rust-lang.org
Cell in std::cell - Rust
A mutable memory location.
Дальнейшее чтение:
- UnsafeCell — единственный тип, который можно легально (но, конечно, небезопасно) менять по разделяемой ссылке. Абсолютно все типы с interiour mutability в конечном счёте опираются именно на него.
- Focusing on ownership — старая (до выхода Rust 1.0) статьяНикитоса Niko Matsakis, в которой он предлагает заменить
- Common Rust Lifetime Misconceptions (мой перевод) — вскользь касается темы уникальности ссылок в пункте 9.
- Deadlock empire — офигенная образовательная игра, в которой нужно взять на себя роль планировщика потоков и, манипулируя порядком исполнения шагов, сломать программу (завести в дедлок, заставить два потока одновременно зайти в критическую секцию и т. д.). Наглядно показывает, насколько сильно в многопотоке мешается неатомарность изменений. Если честно, даже странно, что я раньше её не выкладывал.
- Rustonomicon, конкретно раздел про параллельное исполнение. nuff said.
- Readonly — хтонический макрос от Толяна, который позволяет сделать помеченные поля публичными, но при этом неизменяемыми (извне крейта). Также абьюзит Deref.
- UnsafeCell — единственный тип, который можно легально (но, конечно, небезопасно) менять по разделяемой ссылке. Абсолютно все типы с interiour mutability в конечном счёте опираются именно на него.
- Focusing on ownership — старая (до выхода Rust 1.0) статья
&mut
на &uniq
, убедительно аргументируя, что это куда точнее отражает суть. Это предложение вызвало в своё время бурные обсуждения, но в итоге, как вы видите, так и не было принято. Возможно, и зря.- Common Rust Lifetime Misconceptions (мой перевод) — вскользь касается темы уникальности ссылок в пункте 9.
- Deadlock empire — офигенная образовательная игра, в которой нужно взять на себя роль планировщика потоков и, манипулируя порядком исполнения шагов, сломать программу (завести в дедлок, заставить два потока одновременно зайти в критическую секцию и т. д.). Наглядно показывает, насколько сильно в многопотоке мешается неатомарность изменений. Если честно, даже странно, что я раньше её не выкладывал.
- Rustonomicon, конкретно раздел про параллельное исполнение. nuff said.
- Readonly — хтонический макрос от Толяна, который позволяет сделать помеченные поля публичными, но при этом неизменяемыми (извне крейта). Также абьюзит Deref.
doc.rust-lang.org
UnsafeCell in std::cell - Rust
The core primitive for interior mutability in Rust.
#prog #article #amazingopensource
Алгоритм валидации UTF-8 практически без условных переходов, использующий меньше одной процессорной инструкции на байт входных данных. Реализация доступна как часть библиотеки simdjson
Алгоритм валидации UTF-8 практически без условных переходов, использующий меньше одной процессорной инструкции на байт входных данных. Реализация доступна как часть библиотеки simdjson
GitHub
simdjson/doc/basics.md at master · simdjson/simdjson
Parsing gigabytes of JSON per second : used by Facebook/Meta Velox, the Node.js runtime, ClickHouse, WatermelonDB, Apache Doris, Milvus, StarRocks - simdjson/simdjson
#prog #rust
Хозяйке на заметку
Пара советов по строкам в Rust:
1) Если вам нужно разбить строку по одному из нескольких возможных символов — не спешите расчехлять регулярки, для это задачи вполне хватит стандартной библиотеки. Множество строковых методов навроде {, r}split{, _terminator}, trim{, _start, _end}_matches, find и прочие принимают в качестве аргумента для поиска значение, тип которого реализует пока нестабильный трейт Pattern. В настоящий момент его реализуют
Вызвать `next`?
Нет.
Также вызвать `next_back`?
Нет.
Это всё неполные ответы. Если мы получаем мутабельную ссылку на
Покажу на примере.
Вот первый способ вытащить строку из
Хозяйке на заметку
Пара советов по строкам в 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<_>>();2) Если функция принимает на вход
assert_eq!(result, vec!["He", "", "", ", w", "r", "d!"]);
&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)
}
doc.rust-lang.org
Pattern in std::str::pattern - Rust
A string pattern.
#prog #article
Статья об антипаттерне использования
да, это статья десятилетней давности, ну и что?
Статья об антипаттерне использования
File.exists
.да, это статья десятилетней давности, ну и что?
Paranoidcoding
The File System is Unpredictable
Блог*
Донаты! Донаты?
Я, конечно, совсем ни на что не намекаю, но вот вам номер моей карты:
4274 3200 5402 8520
4274 3200 5402 8520
Блог*
Я, конечно, совсем ни на что не намекаю, но вот вам номер моей карты: 4274 3200 5402 8520
Не переживайте, я не собираюсь возводить paywall, все посты в обозримом будущем продолжат публиковаться на канале
Forwarded from мне не нравится реальность (waffle 🧇🍓)
Хвастаюсь: мой PR добавляющий
[Tracking Issue]
as_str
методы к разным Split
-строковым итераторам недавно смерджил толян![Tracking Issue]
Как же достали люди, которые носят маски, но при этом не прикрывают нос. Они что, правда не понимают, что это всё равно что без маски ходить?