Progrust Library.

// dictionary

NLL(非レキシカルライフタイム).

NLL(非レキシカルライフタイム)は、借用がいつまで続くか(どこまで有効か)を判定する借用チェッカーの解析方式です。

NLL導入前は、借用は字句上のスコープ(レキシカルスコープ){...}の終わりまで有効でした。
NLL導入後は、借用は制御フロー上でその参照が最後に使用される地点までが有効な範囲となりました。

つまり、借用は最後の使用とともに終了するため、参照を保持する変数がまだスコープ内にあっても、その後に同じ値への排他借用を開始するコードが受け入れられます。

fn main() {
    let mut scores = vec![80, 95, 70];

    // -----以下のコードからscoresの共有借用(first)の開始-----
    let first = &scores[0]; 
    println!("最初の点数: {first}");
    // -----上記コードで共有借用(first)の最後の使用となるので、借用はここで終わる-----

    scores.push(60); // 共有借用(first)はここでは無効なのでscoresの排他借用ができる
    println!("全科目: {scores:?}");
} // -----NLL導入前はここまで共有借用(first)の有効範囲だった-----
Playgroundで開く

補足

Dropがある場合の借用範囲

NLLでは制御フロー上でその参照が最後に使用される地点までが有効な範囲となりますが、以下のようなコードはNLLでは範囲外となる箇所で値を変更しようとするとエラーとなります。

struct Logger<'a>(&'a String);

// Dropトレイトを実装
impl<'a> Drop for Logger<'a> {
    fn drop(&mut self) {
        println!("記録: {}", self.0);
    }
}

fn main() {
    let mut s = String::from("hello");
    let logger = Logger(&s);
    s.push('!');   // エラー(E0502)
}
Playgroundで開く

このLogger構造体ではdropを実装しており、dropでは自身の参照が使われる可能性があります。そしてそのdropはスコープの終わりで自動的に呼ばれます。つまりコンパイラはloggerがコードに書かれていなくてもloggerはスコープの終わりまで生きていると判断し、借用を延長します。

導入の経緯とバージョン

NLLはRFC 2094で提案されました。従来のようにソースコードの字句構造(スコープのネスト)からライフタイムを決めるのではなく、コンパイラの中間表現(MIR)の制御フローグラフ上で借用の有効範囲を解析することで、「実際に使われている範囲」に基づいた検査を実現しています。

バージョン 内容
Rust 1.31(2018年12月) Rust 2018エディション向けにNLLを安定化
Rust 1.36(2019年7月) Rust 2015エディションでもNLLを有効化

現在のRustでは、エディションに関わらずNLLに基づいて借用検査が行われます。

この辞書が使われているページ

backlinks 2

  1. 辞書借用
  2. 動かして学ぶRustプログラミング問題集 › 第8章 参照と借用