У Rust є RFC, пов’язаний із нелексичним життям, який затверджено впроваджувати в цю мову протягом тривалого часу. Останнім часом підтримка цієї функції Рустом значно покращилась і вважається повною.
Моє запитання: що саме таке нелексичне життя?
У Rust є RFC, пов’язаний із нелексичним життям, який затверджено впроваджувати в цю мову протягом тривалого часу. Останнім часом підтримка цієї функції Рустом значно покращилась і вважається повною.
Моє запитання: що саме таке нелексичне життя?
Відповіді:
Найпростіше зрозуміти, що таке нелексичне життя, зрозумівши, що таке лексичне життя. У версіях 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
by 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
.