TL;DR

  • ライフタイム注釈を使うと、出力の参照の生存期間が、どの入力の参照に依存するかを明示できる。
  • 同じ注釈を付けても、生存期間が同じという意味ではない。注釈は、参照が参照先より長く存在しないことを保証するためのものである。

内容

項目14の節の 1 つ。

ライフタイム注釈

コードでは、生存期間に 'a、'b、'c のような任意の名前を付けられる。これをライフタイム注釈と呼ぶ。コンパイラは、この名前がコードのどのスコープを指すかを内部で解釈して、生存期間を管理する。開発者はこの情報を直接見ることはできない。

ライフタイム注釈を使うと、出力の参照の生存期間がどの入力の参照に依存するかを明示できる。例外は 'static である('static ライフタイム)。

引数で参照を 1 つ受け取り、参照を 1 つ返す関数では、'static を除けば使える生存期間は入力のものだけなので、次のようになる。

pub fn first<'a>(data: &'a [Item]) -> Option<&'a Item> {
    // ...
}

ただし、このケースはよくある形で、生存期間省略ルールに当てはまるので、注釈を書く必要はない。

pub fn first(data: &[Item]) -> Option<&Item> {

引数で参照を 2 つ受け取り、参照を 1 つ返す関数では、生存期間が一意に決まらないので注釈が必須である。

pub fn find<'a>(haystack: &'a [u8], needle: &'a [u8]) -> Option<&'a [u8]>
pub fn find<'a, 'b>(haystack: &'a [u8], needle: &'b [u8]) -> Option<&'a [u8]>
 
// 注釈がないとコンパイルエラー
pub fn find(haystack: &[u8], needle: &[u8]) -> Option<&[u8]> {

同じ注釈は同じ生存期間ではない

ライフタイム注釈が同じでも、生存期間が同じということではない。注釈の目的は、アイテムへの参照がアイテム自身より長く存在しないことを保証することである。

入力と出力に同じ注釈を付けると、出力は入力より長く生存できないことを表す。2 つの引数に同じ注釈を付けると、出力は生存期間の短いほうの引数より長く生存できないことを表す。

{
    let outer = Item { contents: 7 };
    {
        let inner = Item { contents: 8 };
        {
            let min = smaller(&inner, &outer);
        } // min がドロップされる
    } // inner がドロップされる
} // outer がドロップされる

一方、戻り値が一方の引数だけを参照するなら、戻り値の生存期間がもう一方の生存期間を含む必要はない。

pub fn find<'a, 'b>(haystack: &'a [u8], needle: &'b [u8]) -> Option<&'a [u8]> {
{
    let haystack = b"123456789"; // 生存期間 'a の始まり
    let found = {
        let needle = b"234"; // 生存期間 'b の始まり
        find(haystack, needle)
    }; // 生存期間 'b の終わり
} // 生存期間 'a の終わり

キーワード