Progrust Library.

// dictionary

ライフタイム注釈.

ライフタイム注釈は、複数の参照のライフタイムがどう関係しているかをコンパイラに伝える記法です。名前はアポストロフィで始まる短い小文字(慣例として最初は'a)で、ジェネリクスの型引数と同じように<'a>で宣言してから、&'a Tのように&の直後に書いて使います1。注釈は値が生きる長さを変えるものではなく、借用チェッカーが検査に使う「関係の宣言」です2。

// 戻り値は a か b のどちらかを指す。3つの参照を同じ 'a で結び、
// 「戻り値は a と b の両方が生きている間だけ有効」と宣言する
fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.chars().count() >= b.chars().count() { a } else { b }
}

// 参照を持つ構造体。インスタンスは shop_name の参照先より長く生きられない
struct Receipt<'a> {
    shop_name: &'a str,
}

impl<'a> Receipt<'a> {
    // &self があるので戻り値の注釈は省略できる(省略規則3)
    fn header(&self) -> &str {
        self.shop_name
    }
}

fn main() {
    let shop = String::from("プログルストア新宿店");
    let receipt = Receipt { shop_name: &shop };
    let longest = longer("りんご", "グレープフルーツ");
    println!("{} / 長い方: {}", receipt.header(), longest);
}
Playgroundで開く

指定する場所

注釈はジェネリックパラメータなので、使う前にその項目の<>で宣言します。宣言できる場所と書き方は次のとおりです。

場所 宣言 使用
関数のシグネチャ 関数名の直後 fn longer<'a> 引数・戻り値の型 &'a str
構造体・列挙型 型名の直後 struct Receipt<'a> フィールドの型 &'a str
implブロック implの直後 impl<'a> 対象の型 Receipt<'a> と各メソッド

型引数と併用する場合、ライフタイムパラメータは型パラメータより前に書きます(<'a, T>は可、<T, 'a>は不可)3。'staticと'_は特別な意味を持つため、パラメータ名として宣言することはできません3。

参照をフィールドに持つ構造体では注釈が必須で、struct Receipt { shop_name: &str }のように省くとE0106になります4。implブロック側では、構造体の型の一部になっているライフタイムをimpl<'a> Receipt<'a>のように改めて宣言します5。

ライフタイム省略規則

関数シグネチャの注釈は、コンパイラが次の3つの規則で補えるときは省略できます6。

  1. 引数側の省略された各ライフタイムは、それぞれ別のライフタイムパラメータになります
  2. 引数側のライフタイムが(省略・明示を問わず)ちょうど1つなら、それが戻り値の省略されたライフタイムすべてに割り当てられます
  3. メソッドで受け手が&selfまたは&mut selfなら、その参照のライフタイムが戻り値の省略されたライフタイムすべてに割り当てられます
省略した形 展開後 適用される規則
fn first(s: &str) -> &str fn first<'a>(s: &'a str) -> &'a str 1 → 2
fn header(&self, label: &str) -> &str fn header<'a, 'b>(&'a self, label: &'b str) -> &'a str 1 → 3
fn longer(a: &str, b: &str) -> &str 展開できない(E0106) 1のみ。2も3も当てはまらない

3つ目のように引数が複数あって戻り値がどれを指すか決められないときは、先頭のコード例のlongerのように手で注釈を書きます(エラーの実例は補足を参照)。

'static

'staticは「プログラムの実行期間全体にわたって有効でありうる」ことを表す特別なライフタイムです7。文字列リテラルはバイナリに直接埋め込まれるため&'static strになり、プログラム終了まで存在する静的変数を借用した参照も'staticです。const・staticの宣言で参照型を書くときは、注釈を省いても暗黙に'staticになります8。

static SHOP_NAME: &str = "プログルストア"; // &'static str(省略可)

fn shop_name() -> &'static str {
    // 引数がないので省略規則は使えないが、'static なら明示して返せる
    SHOP_NAME
}

fn main() {
    let greeting: &'static str = "いらっしゃいませ";
    println!("{}: {}", shop_name(), greeting);
}
Playgroundで開く

補足

省略できない例(E0106)

引数が2つとも参照で、戻り値がどちらを指すか規則では決められないため、注釈を省くとコンパイルエラーになります。

// エラー: E0106(戻り値が a と b のどちらのライフタイムか決められない)
fn longer(a: &str, b: &str) -> &str {
    if a.len() >= b.len() { a } else { b }
}
Playgroundで開く
プレースホルダー '_

'_は「ここにライフタイムがあるが、推論に任せる」ことを明示するプレースホルダーです6。fn first(s: &'_ str) -> &'_ strは省略形と同じ意味になります。ライフタイムを持つ型をパス(Path)で書くときは、fn make(buf: &str) -> Receipt<'_>のように'_を付けて「参照を借りている型である」ことを見せる書き方が推奨されています6。同様に、implブロックの中でライフタイム名を一度も使わないなら、impl<'a> Receipt<'a>の代わりにimpl Receipt<'_>と書けます。

境界としての 'static(T: 'static)

'staticはトレイト境界と同じ位置にライフタイム境界としても書けます。T: 'aは「Tが持つすべてのライフタイムパラメータが'aより長く生きる」という意味で、T: 'staticなら「Tは'staticでない参照を一切含まない」ことを要求します9。i32やStringのように参照を持たない型はこの境界を自動的に満たすため、T: 'staticは「'staticな参照しか受け付けない」のではなく、「いつ破棄されるか分からない借用を含まない型」を表すと読むのが正確です。スレッドをまたいで値を渡すAPIなどで要求されます。

ライフタイムどうしの関係 'a: 'b

複数のライフタイムパラメータの間に「'aは少なくとも'bと同じ長さ生きる」という関係を付けたいときは、fn f<'a: 'b, 'b>(...)やwhere 'a: 'bのように書きます9。意味の詳細はライフタイムの補足を参照してください。

Footnotes

  1. The Rust Programming Language - Lifetime Annotation Syntax “The names of lifetime parameters must start with an apostrophe (’) and are usually all lowercase and very short, like generic types. Most people use the name ’a for the first lifetime annotation. We place lifetime parameter annotations after the & of a reference” / “We declare the name of the generic lifetime parameter inside angle brackets after the name of the struct so that we can use the lifetime parameter in the body of the struct definition.” ↩

  2. 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.” ↩

  3. The Rust Reference - Generic parameters “The order of generic parameters is restricted to lifetime parameters and then type and const parameters intermixed.” / “’_ and ’static are not valid lifetime parameter names.” ↩ ↩2

  4. Error code E0106 “This error indicates that a lifetime is missing from a type.” / struct Foo1 { x: &bool } // ^ expected lifetime parameter / “The lifetime elision rules require that any function signature with an elided output lifetime must either have: exactly one input lifetime, or, multiple input lifetimes, but the function must also be a method with a &self or &mut self receiver” ↩

  5. The Rust Programming Language - Lifetime Annotations in Method Definitions “Lifetime names for struct fields always need to be declared after the impl keyword and then used after the struct’s name because those lifetimes are part of the struct’s type.” ↩

  6. The Rust Reference - Lifetime elision “lifetime arguments can be elided in function item, function pointer, and closure trait signatures.” / “Each elided lifetime in the parameters becomes a distinct lifetime parameter.” / “If there is exactly one lifetime used in the parameters (elided or not), that lifetime is assigned to all elided output lifetimes.” / “If the receiver has type &Self or &mut Self, then the lifetime of that reference to Self is assigned to all elided output lifetime parameters.” / “The placeholder lifetime, ’, can also be used to have a lifetime inferred in the same way. For lifetimes in paths, using ’ is preferred.” / fn new1(buf: &mut [u8]) -> Thing<'_>; // elided - preferred ↩ ↩2 ↩3 ↩4

  7. The Rust Programming Language - The Static Lifetime “’static, which denotes that the affected reference can live for the entire duration of the program. All string literals have the ’static lifetime” / “The text of this string is stored directly in the program’s binary, which is always available.” / “Most of the time, an error message suggesting the ’static lifetime results from attempting to create a dangling reference or a mismatch of the available lifetimes. In such cases, the solution is to fix those problems, not to specify the ’static lifetime.” ↩ ↩2

  8. The Rust Reference - Lifetime elision - const and static elision “Both constant and static declarations of reference types have implicit ’static lifetimes unless an explicit lifetime is specified.” ↩

  9. The Rust Reference - Lifetime bounds “T: ’a means that all lifetime parameters of T outlive ’a. For example, if ’a is an unconstrained lifetime parameter, then i32: ’static and &’static str: ’a are satisfied, but Vec<&’a ()>: ’static is not.” / “The bound ’a: ’b is usually read as ’a outlives ’b.” ↩ ↩2

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

backlinks 2

  1. 辞書ライフタイム
  2. 本動かして学ぶRustプログラミング問題集 › 第22章 ライフタイム