スタティックライフタイム

Rustにはいくつかの予約されたライフタイム名があります。その1つがstaticで、2つの状況で使用することがあります。

// A reference with 'static lifetime:
let s: &'static str = "hello world";

// 'static as part of a trait bound:
fn generic<T>(x: T) where T: 'static {}

2つの状況におけるstaticは微妙に異なる意味を持っており、Rustを学ぶときの混乱の元になっています。 いくつかの例とともにそれぞれの使い方を見てみましょう。

参照のライフタイム

参照のライフタイムが'staticであることは、参照が指し示す値がプログラムの実行中に渡って生き続けることを示します。 また、より短いライフタイムに圧縮することも可能です。

'staticライフタイムを持つ変数を作るには下記の2つ方法があります。 どちらの場合も、値は読み取り専用のメモリ領域に格納されます。

  • static宣言とともに定数を作成する。
  • 文字列リテラルで&'static str型を持つ変数を作成する。

では、それぞれの方法の例を見ていきましょう。

// Make a constant with `'static` lifetime.
// `'static`ライフタイムを持つ定数を作成
static NUM: i32 = 18;

// Returns a reference to `NUM` where its `'static`
// lifetime is coerced to that of the input argument.
// `NUM`への参照を返す。ライフタイムは`'static`から引数の
// ライフタイムへと圧縮されている。
fn coerce_static<'a>(_: &'a i32) -> &'a i32 {
    &NUM
}

fn main() {
    {
        // Make a `string` literal and print it:
        // 文字列リテラルを用いて変数を作成し、プリントする
        let static_string = "I'm in read-only memory";
        println!("static_string: {}", static_string);

        // When `static_string` goes out of scope, the reference
        // can no longer be used, but the data remains in the binary.
        // `static_string`がスコープから抜けると、参照は使用することが
        // できなくなるが、データはバイナリ中に残る。
    }

    {
        // Make an integer to use for `coerce_static`:
        // `coerce_static`関数を呼び出すために、整数を作成
        let lifetime_num = 9;

        // Coerce `NUM` to lifetime of `lifetime_num`:
        // `NUM`を`lifetime_num`のライフタイムへと圧縮
        let coerced_static = coerce_static(&lifetime_num);

        println!("coerced_static: {}", coerced_static);
    }

    println!("NUM: {} stays accessible!", NUM);
}

トレイト境界

トレイト境界としての'staticは型が非静的な参照を含まないことを意味します。 言い換えると、レシーバはその型をいくらでも長く保持することができ、意図的にドロップするまでは決して無効になることはないということです。

次のポイントを押さえておきましょう。所有権のある値が'staticライフタイム境界をパスするとしても、その値への参照が'staticライフタイム境界をパスするとは限りません。

use std::fmt::Debug;

fn print_it( input: impl Debug + 'static ) {
    println!( "'static value passed in is: {:?}", input );
}

fn main() {
    // i is owned and contains no references, thus it's 'static:
    // i は所有されていて、かつ参照を含まないので 'static
    let i = 5;
    print_it(i);

    // oops, &i only has the lifetime defined by the scope of
    // main(), so it's not 'static:
    // おっと、&i は main() で定義されたライフタイムしかもたないため 'static ではない
    print_it(&i);
}

コンパイラのメッセージはこのようになります、

error[E0597]: `i` does not live long enough
エラー[E0597]: `i`は十分なライフタイムを持っていません
  --> src/lib.rs:15:15
   |
15 |     print_it(&i);
   |     ---------^^--
   |     |         |
   |     |         borrowed value does not live long enough
   |     |         借用した値のライフタイムが不足
   |     argument requires that `i` is borrowed for `'static`
   |     引数は`i`が`'static`として借用されることを要求する
16 | }
   | - `i` dropped here while still borrowed
   |   `i`は借用されたままここでドロップされる

参照

'static 定数