// dictionary
ライフタイム.
ライフタイム(lifetime)は、参照が有効であり続ける期間です。Rustのすべての参照はライフタイムを持ち、その多くは型と同じように明示せずともコンパイラが推論します1。借用チェッカーは「参照が使われている間、参照先の値がまだ生きているか」をコンパイル時に検査し、参照先が先に破棄されるコードをエラーにします2。
fn main() {
let store_name; // 参照を入れる変数(この時点では空)
{
let name = String::from("プログルストア新宿店");
store_name = &name; // エラー: E0597(name は内側のブロックを抜けると破棄される)
} // ← name はここで破棄されるが…
println!("{store_name}"); // …参照はここでまだ使われている
}Playgroundで開くコンパイラが追跡しているもの
ライフタイムの検査は、次の2つの期間を突き合わせて行われます。
| 追跡対象 | 何で決まるか | 上の例では |
|---|---|---|
| 参照先の値が生きている期間 | 値を所有する変数のスコープ。ブロックを抜けると破棄される3 | nameは内側のブロックの終わりまで |
| 参照が使われている期間(ライフタイム) | 参照が作られてから、制御フロー上でその参照がまだ使われうる地点まで(概ね最後の使用地点まで)4 | store_nameはprintln!まで |
参照のライフタイムは、参照先の値が生きている期間に収まっていなければなりません。上の例ではこの条件が崩れるため、「nameは十分長く生きていない(does not live long enough)」という形でエラーになります5。逆に言えば、借用した参照を使い終えるより長く元の値を生かしておけば、検査はそのまま通ります。
fn main() {
let name = String::from("プログルストア新宿店"); // name を外側のブロックへ移す
let store_name = &name; // 参照は name より長く生きない
println!("{store_name}"); // OK
}Playgroundで開くダングリング参照・NLLとの関係
ライフタイムの主な目的は、破棄済みの値を指すダングリング参照を作らせないことです2。「参照先の値が先に死ぬ」のが問題の本質で、上の例のように破棄前に検出されればE0597、関数のローカルな値への参照を戻り値として返せばE0515になります(詳細はダングリング参照と借用チェッカーを参照)。
また、参照のライフタイムは変数の字句上のスコープではなく、NLL(非レキシカルライフタイム)により「制御フロー上でその参照が最後に使われる地点まで」として判定されます4。そのため参照を保持する変数がまだスコープ内にあっても、使い終えた後なら元の値を書き換えられます。
補足
ライフタイムは推論されるのが基本
ライフタイムが明示的な注釈なしで済むのは、1つの関数の中ではコンパイラが参照の作成地点と最後の使用地点をすべて見渡せるからです1。関数の境界をまたぐ場合(参照を受け取って参照を返す関数や、参照を持つ構造体)は、呼び出し元と呼び出し先を別々に検査するため、関係を伝える注釈が必要になることがあります7。ただし典型的なパターンは省略規則でまかなえるため、多くの関数は注釈なしで書けます8(ライフタイム注釈を参照)。
ジェネリックパラメータとしてのライフタイム
ライフタイムはジェネリクスのパラメータの一種で、型引数Tが「どんな型でもよい」のと同じように、ライフタイムパラメータ'aは「どんな期間でもよい」を表します1。トレイト境界と同様に、'a: 'b('aは少なくとも'bと同じ長さ生きる。慣用的に「'aは'bをoutliveする」と読みます)というライフタイム境界で期間どうしの関係を制約することもできます9。
値が破棄される時点との対応
参照先の値が「生きている期間」は、その値を所有する変数がドロップスコープを抜ける時点で終わります。同じスコープの変数は宣言と逆順に破棄されるため3、後から宣言した変数が先に宣言した変数を参照する分には問題になりません。逆向き(先に宣言した変数に、後から宣言した値への参照を入れる)は、上の例のようにE0597になります。また、参照を保持している側の値(参照を含む構造体など)がDropを実装している場合は、その値が破棄される時点も「参照の使用」とみなされ、借用の期間がそこまで延びます4(借用の「借用はいつまで続くか」を参照)。
Footnotes
-
The Rust Programming Language - Validating References with Lifetimes “every reference in Rust has a lifetime, which is the scope for which that reference is valid. Most of the time, lifetimes are implicit and inferred, just like most of the time, types are inferred.” / “Lifetimes are another kind of generic that we’ve already been using.” ↩ ↩2 ↩3
-
The Rust Programming Language - Preventing Dangling References with Lifetimes “The main aim of lifetimes is to prevent dangling references” / “The Rust compiler has a borrow checker that compares scopes to determine whether all borrows are valid.” ↩ ↩2
-
The Rust Reference - Destructors “When an initialized variable or temporary goes out of scope, its destructor is run or it is dropped.” / “When control flow leaves a drop scope all variables associated to that scope are dropped in reverse order of declaration” ↩ ↩2
-
RFC 2094 - Non-lexical lifetimes “these are lifetimes that are based on the control-flow graph, rather than lexical scopes.” / “we will consider lifetimes as a set of points in the control-flow graph” / “a variable is live if the current value that it holds may be used later.” / drop-liveness: “indicates when a variable’s value may be dropped in the future … requiring only those lifetimes to be live that are not marked as may-dangle” ↩ ↩2 ↩3
-
Error code E0597 “This error occurs because a value was dropped while it was still borrowed.” ↩
-
The Rust Programming Language - Lifetime Annotation Syntax “Lifetime annotations don’t change how long any of the references live. Rather, they describe the relationships of the lifetimes of multiple references to each other without affecting the lifetimes.” ↩
-
The Rust Programming Language - Generic Lifetimes in Functions “we don’t know the concrete lifetimes of the references that will be passed in, so we can’t look at the scopes” / “The borrow checker can’t determine this either, because it doesn’t know how the lifetimes of x and y relate to the lifetime of the return value.” ↩
-
The Rust Reference - Lifetime elision “Rust has rules that allow lifetimes to be elided in various places where the compiler can infer a sensible default choice.” ↩
-
The Rust Reference - Lifetime bounds “The bound ’a: ’b is usually read as ’a outlives ’b. ’a: ’b means that ’a lasts at least as long as ’b, so a reference &’a () is valid whenever &’b () is valid.” ↩
この辞書が使われているページ
backlinks 11