Что такое нелексические времена жизни? - PullRequest
0 голосов
/ 09 мая 2018

Rust имеет RFC , относящийся к нелексическим временам жизни, который был одобрен для для использования в языке в течение длительного времени. В последнее время , поддержка этой функции в Rust значительно улучшилась и считается завершенной.

Мой вопрос: что такое нелексическое время жизни?

1 Ответ

0 голосов
/ 09 мая 2018

Легче всего понять, что такое нелексические времена жизни, поняв, что такое лексические времена жизни. В версиях 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) , необходимо переписать компилятор . Затем необходимо было переписать средство проверки заимствования (a.k.a. "loanck") для использования 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 1.31 (выпущен 2018-12-06) вам необходимо подписаться на редакцию Rust 2018 в своем Cargo.toml:

[package]
name = "foo"
version = "0.0.1"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"

Начиная с Rust 1.36, редакция Rust 2015 также допускает нелексические времена жизни.

Текущая реализация нелексических времен жизни находится в «режиме миграции». Если проверка заимствования NLL прошла, компиляция продолжается. Если это не так, вызывается предыдущий заемщик. Если старая программа проверки заимствования разрешает ввод кода, выводится предупреждение, информирующее вас о том, что ваш код может сломаться в будущей версии Rust и должен быть обновлен.

В ночных версиях Rust вы можете включить принудительную поломку с помощью флага функции:

#![feature(nll)]

Вы даже можете подписаться на экспериментальную версию NLL, используя флаг компилятора -Z polonius.

Пример реальных проблем, решаемых нелексическими временами жизни

...