Що таке нелексичне життя?


88

У Rust є RFC, пов’язаний із нелексичним життям, який затверджено впроваджувати в цю мову протягом тривалого часу. Останнім часом підтримка цієї функції Рустом значно покращилась і вважається повною.

Моє запитання: що саме таке нелексичне життя?

Відповіді:


130

Найпростіше зрозуміти, що таке нелексичне життя, зрозумівши, що таке лексичне життя. У версіях 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ніколи не використовується ! Проблема полягає в тому, що запозичення scoresby scoreє лексичним - воно триває до кінця блоку, в якому він міститься:

fn main() {
    let mut scores = vec![1, 2, 3]; //
    let score = &scores[0];         //
    scores.push(4);                 //
                                    // <-- score stops borrowing here
}

Нелексичні періоди життя це виправляють, покращуючи компілятор, щоб зрозуміти цей рівень деталізації. Тепер компілятор може більш точно визначити, коли потрібна позика, і цей код буде скомпільовано.

Чудова річ у нелексичних життях полягає в тому, що коли вони ввімкнені, ніхто ніколи не буде думати про них . Це просто стане «тим, що робить Іржа», і речі (сподіваємось) просто спрацюють.

Чому було дозволено лексичне життя?

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".

Non-лексичні часи життя ніколи не були призначені , щоб бути «користувач-облицювальний» особливість, сама по собі . Вони здебільшого зросли в нашій свідомості через маленькі папірці, які ми отримуємо від їх відсутності. Їх назва здебільшого була призначена для внутрішніх цілей розвитку, і її зміна для маркетингових цілей ніколи не була пріоритетом.

Так, але як я ним користуюся?

У Rust 1.31 (випущено 06.12.2018) вам потрібно підписатися на видання 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.

Зразок реальних проблем, вирішених нелексичним життям


11
Думаю, варто підкреслити, що, можливо, проти інтуїтивно, нелексичні періоди життя стосуються не терміну життя змінних, а часу життя запозичень. Або, інакше сказано, «Нелексичні довічні періоди» - це про декорреляцію періодів життя змінних та запозичень ... якщо я не помиляюся? (але я не думаю, що NLL змінюється, коли виконується деструктор)
Матьє М.

2
" Цікаво, що певні гарні зразки були розроблені через лексичне життя " - тоді я припускаю, що існує ризик того, що існування НЛЛ може зробити майбутні гарні зразки набагато складнішими для ідентифікації?
eggyal

1
@eggyal це, звичайно, можливість. Проектування в рамках набору обмежень (навіть якщо довільне!) Може призвести до нових, цікавих конструкцій. Без цих обмежень ми могли б повернутися до наших існуючих знань і зразків і ніколи не навчитися чи досліджувати, щоб знайти щось нове. З огляду на це, мабуть, хтось подумає "о, хеш обчислюється двічі, я можу це виправити", і API буде створений, але користувачам може бути важче знайти API в першу чергу. Я сподіваюся, що такі інструменти, як кліпі, допомагають цим людям.
Шепмастер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.