Rustには、長い間この言語での実装が承認されている非字句の存続期間に関連するRFCがあります。最近、Rustによるこの機能のサポートは大幅に改善され、完全であると見なされています。
私の質問は、非字句の寿命とは正確には何ですか?
回答:
これは、非語彙的寿命は何を理解することであるかを理解するのが最も簡単だ字句寿命です。非字句の有効期間が存在する前のバージョンの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が行うこと」になり、物事は(うまくいけば)うまくいくでしょう。
Rustは、既知の安全なプログラムのみをコンパイルできるようにすることを目的としています。ただし、安全なプログラムのみを正確に許可し、安全でないプログラムを拒否することは不可能です。そのために、Rustは保守的であるという側面で誤りを犯します:いくつかの安全なプログラムは拒否されます。語彙の寿命はその一例です。
ブロックの知識は「取るに足らない」ものであるのに対し、データフローの知識はそれほど重要ではないため、レキシカルライフタイムはコンパイラに実装するのがはるかに簡単でした。「中間レベルの中間表現」(MIR)を導入して利用するには、コンパイラを書き直す必要がありました。次に、抽象構文木(AST)の代わりにMIRを使用するように、借用チェッカー(別名「borrowck」)を書き直す必要がありました。次に、借用チェッカーのルールを洗練して、よりきめ細かくする必要がありました。
字句の存続期間が常にプログラマーの邪魔になるとは限りません。また、字句の存続期間が煩わしい場合でも、回避する方法はたくさんあります。多くの場合、これには中括弧またはブール値の追加が含まれていました。これにより、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);
}
}
}
ただし、このコードはキーのハッシュを2回計算するため、非効率的です。字句の有効期間のために作成されたソリューションは、より短く、より効率的です。
fn example(mut map: HashMap<i32, i32>, key: i32) {
*map.entry(key).or_insert(0) += 1;
}
値の有効期間は、値が特定のメモリアドレスにとどまる期間です(詳細については、値とその値への参照を同じ構造体に格納できないのはなぜですか?を参照してください)。非字句の有効期間と呼ばれる機能は、値の有効期間を変更しないため、有効期間を非字句にすることはできません。これらの値の借用の追跡とチェックをより正確にするだけです。
この機能のより正確な名前は、「非字句借用」である可能性があります。一部のコンパイラ開発者は、基礎となる「MIRベースの借用」を参照しています。
非字句の存続期間は、それ自体が「ユーザー向け」機能であることを意図したものではありませんでした。彼らの不在から私たちが得る小さなペーパーカットのために、彼らはほとんど私たちの心の中で大きくなりました。それらの名前は主に内部開発の目的で意図されており、マーケティングの目的で名前を変更することは決して優先事項ではありませんでした。
Rust 1.31(2018-12-06にリリース)では、Cargo.tomlでRust2018エディションにオプトインする必要があります。
[package]
name = "foo"
version = "0.0.1"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"
Rust 1.36の時点で、Rust2015エディションは非字句のライフタイムも有効にします。
非字句ライフタイムの現在の実装は「移行モード」です。NLLボローチェッカーが合格すると、コンパイルが続行されます。そうでない場合は、前の借用チェッカーが呼び出されます。古い借用チェッカーがコードを許可している場合、警告が出力され、コードがRustの将来のバージョンで破損する可能性があり、更新する必要があることを通知します。
Rustの夜間バージョンでは、機能フラグを介して強制的な破損にオプトインできます。
#![feature(nll)]
コンパイラフラグを使用して、NLLの実験バージョンにオプトインすることもできます-Z polonius
。