
Size: a a a






// код опущен для краткостиОчевидно, этот код не работает. И действительно, если мы запустим эту программу, то один из ассертов запустит панику. В данном случае это будет... Будет... Погодите-ка, оно работает? Что?! Но ведь в 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));
}
Vec. Как это работает? Неужели реализация этих методов скопирована с методов для слайсов? Конечно, нет.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 проверяет, используя концепции владения и заимствования. Но это не единственный способ!&) ссылкам. И действительно, если мы откроем документацию к, скажем, AtomicU32, то мы увидим, что практически все методы принимают &self.
Tricky? Какое-то атомарное значение? Нет, field — это просто u32, а сделать атомарной саму структуру Simple мы не можем. RefCell? Тоже нет. Метод RefCell::borrow возвращает значение особого типа, которое реализует Deref в оборачиваемые данные и которое в деструкторе уменьшает счётчик неизменяемых доступов. Но, напомним, в методе deref нам нужно вернуть именно ссылку. Единственный способ получить получить ссылку на внутреннее значение в RefCell — это вызвать borrow{, _mut}, который вернёт тот прокси-тип. Но Rust просто не даст нам вернуть ссылку на локальную переменную! Выходит, этот вариант тоже отпадает. Что же тогда используется в Tricky?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 выбирает нужное поле.&mut-ссылку на него, но нельзя через &-ссылку. Это называется inherited mutability (наследуемая изменяемость), поскольку значение "наследует" изменяемость обёртки. Однако, как мы видели, значение может быть изменяемым и через &-ссылку. Это называется interiour mutability (внутренняя изменяемость). Это нужная на практике вещь, поскольку Rc/Arc и мьютекс с безопасным интерфейсом без неё создать невозможно.
&mut на &uniq, убедительно аргументируя, что это куда точнее отражает суть. Это предложение вызвало в своё время бурные обсуждения, но в итоге, как вы видите, так и не было принято. Возможно, и зря.





&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!"]);
&mut std::str::Chars, что она может с ним сделать?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)
}
File.exists.