Что такое нелексические времена жизни?
Rust имеет RFC, относящийся к нелексическим временам жизни, который был утвержден для использования в языке в течение длительного времени. В последнее время поддержка этой функции в Rust значительно улучшилась и считается завершенной.
У меня вопрос: что такое нелексическое время жизни?
1 ответ
Легче всего понять, что такое нелексические времена жизни, понимая, что такое лексические времена жизни. В версиях Rust до появления нелексических времен жизни этот код завершится ошибкой:
fn main() {
let mut scores = vec![1, 2, 3];
let score = &scores[0];
scores.push(4);
}
Компилятор Rust видит это scores
заимствовано score
переменная, поэтому она запрещает дальнейшую мутацию scores
:
error[E0502]: cannot borrow `scores` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let score = &scores[0];
| ------ immutable borrow occurs here
4 | scores.push(4);
| ^^^^^^ mutable borrow occurs here
5 | }
| - immutable borrow ends here
Тем не менее, человек может легко увидеть, что этот пример слишком консервативен: score
никогда не используется! Проблема в том, что заимствование scores
от score
является лексическим - оно длится до конца блока, в котором оно содержится:
fn main() {
let mut scores = vec![1, 2, 3]; //
let score = &scores[0]; //
scores.push(4); //
// <-- score stops borrowing here
}
Нелексические времена жизни исправляют это, улучшая компилятор для понимания этого уровня детализации. Компилятор теперь может более точно определить, когда требуется заимствование, и этот код скомпилируется.
Замечательная вещь о нелексических жизнях состоит в том, что, когда они включены, никто никогда не будет думать о них. Это просто станет "тем, что делает Rust", и все будет (надеюсь) просто работать.
Почему лексические времена жизни были разрешены?
Rust предназначен для компиляции только известных безопасных программ. Однако невозможно точно разрешить только безопасные программы и отклонить небезопасные. С этой целью Rust ошибается на стороне консервативности: некоторые безопасные программы отклоняются. Лексические времена жизни являются одним из примеров этого.
Лексические времена жизни было намного проще реализовать в компиляторе, потому что знание блоков "тривиально", а знание потока данных - меньше. Компилятор нужно было переписать, чтобы ввести и использовать "промежуточное представление среднего уровня" (MIR). Затем необходимо было переписать средство проверки заимствований (иначе "заемщик"), чтобы использовать MIR вместо абстрактного синтаксического дерева (AST). Затем правила проверки заимствований должны были быть уточнены, чтобы быть более детальными.
Лексические времена жизни не всегда мешают программисту, и есть много способов обойти лексические времена жизни, когда они это делают, даже если они раздражают. Во многих случаях это включало добавление дополнительных фигурных скобок или логическое значение. Это позволило выпустить Rust 1.0 и быть полезным в течение многих лет, прежде чем были внедрены нелексические времена жизни.
Интересно, что определенные хорошие образцы были разработаны из-за лексических времен жизни. Ярким примером для меня является entry
образец Этот код завершается с ошибкой до нелексического времени жизни и компилируется с ним:
fn example(mut map: HashMap<i32, i32>, key: i32) {
match map.get_mut(&key) {
Some(value) => *value += 1,
None => {
map.insert(key, 1);
}
}
}
Однако этот код неэффективен, потому что он вычисляет хэш ключа дважды. Решение, созданное из- за лексических времен жизни, короче и эффективнее:
fn example(mut map: HashMap<i32, i32>, key: i32) {
*map.entry(key).or_insert(0) += 1;
}
Название "нелексические времена жизни" мне не подходит
Время жизни значения - это промежуток времени, в течение которого значение остается по определенному адресу памяти (см. Почему я не могу сохранить значение и ссылку на это значение в той же структуре? Для более подробного объяснения). Функция, известная как нелексические времена жизни, не меняет времени жизни каких-либо значений, поэтому она не может сделать времена жизни нелексическими. Это только делает отслеживание и проверку заимствований этих значений более точными.
Более точное название функции может быть "нелексические заимствования ". Некоторые разработчики компиляторов ссылаются на базовый "заем на основе MIR".
Нелексические времена жизни, как таковые, никогда не задумывались как функция, ориентированная на пользователя. Они в основном выросли в наших умах из-за маленьких бумажек, которые мы получаем от их отсутствия. Их имя было в основном предназначено для внутренних целей развития, и изменение его в маркетинговых целях никогда не было приоритетом.
Да, но как мне это использовать?
Вы не в стабильном Rust. В какой-то момент он будет включен, и все будут его использовать, и вам ничего не нужно будет делать.
В Rust 1.31 (выпуск запланирован на 2018-12-06 и доступен в бета-версии сейчас), вам необходимо подписаться на выпуск Rust 2018 в вашем Cargo.toml:
[package]
name = "foo"
version = "0.0.1"
authors = ["An Devloper <an.devloper@example.com.com>"]
edition = "2018"
В ночных версиях Rust вы можете подписаться на NLL с помощью функции:
#![feature(nll)]
Вы даже можете подписаться на экспериментальную версию NLL, используя флаг компилятора -Z polonius
,
Пример реальных проблем, решаемых нелексическими временами жизни
- Rust заимствовать HashMap длится за пределами его возможностей?
- Почему HashMap::get_mut() становится владельцем карты для остальной части области?
- Не может заимствовать как неизменяемый, потому что он также заимствован как изменяемый в аргументах функции
- Как обновить или вставить на Vec?
- Есть ли способ освободить привязку до того, как она выйдет из области видимости?
- Невозможно получить изменяемую ссылку при итерации рекурсивной структуры: нельзя заимствовать как изменяемый более одного раза за раз
- При возврате результата использования StdinLock, почему был сохранен заем для stdin?
- Смещенная по ошибке ошибка при деконструкции бокса пар