TL;DR

  • 'static は、プログラムの実行中ずっと有効な生存期間を表す。
  • 'static な参照は、static 変数への参照、const な値の昇格、Box::leak によるヒープ上の値のリークのいずれかで得られる。

内容

項目14の節の 1 つ。

‘static が必要になる場面

引数で参照を受け取らないのに参照を返す関数では、'static を指定する必要がある。これにより、戻り値が決してスコープから外れないことを保証できる。

pub fn the_answer() -> &Item          // コンパイルエラー
pub fn the_answer() -> &'static Item  // OK

‘static な値を得る方法

1 つ目は、static なグローバル変数への参照を取ることである。コンパイラは、static な値がプログラムの実行中つねに同じアドレスにあり、ムーブしないことを保証する。そのため、その参照の生存期間は常に 'static になる。

static ANSWER: Item = Item { contents: 42 };
 
pub fn the_answer() -> &'static Item {
    &ANSWER
}

2 つ目は、const な値への参照を 'static に昇格させることである。ただし、注意点が 2 つある。まず、型がデストラクタ(Drop)や内部可変性(RefCell、Mutex、RwLock)を持つ場合は昇格されない。そのため、次のコードはコンパイルエラーになる。

pub struct Wrapper(pub i32);
 
impl Drop for Wrapper {
    fn drop(&mut self) {}
}
 
const ANSWER: Wrapper = Wrapper(42);
 
pub fn the_answer() -> &'static Wrapper {
    &ANSWER
}

次に、const の値はメモリアドレスが固定されない。値は変わらないがコピーが作られうるので、常に同じアドレスを指す保証はない。

3 つ目は、ヒープに確保した値を 'static として扱うことである。通常、Box<T> に入れた値はドロップされないことを保証できないので、この条件を満たさない。

{
    let boxed = Box::new(Item { contents: 42 });
    let r: &'static Item = &boxed; // コンパイルエラー
    println!("'static item is {r:?}");
}

しかし Box::leak を使えば、所有している Box<T> を消費して、T への可変参照に変えられる。この値には所有者がいないので、ドロップされないことを保証できる。

{
    let boxed = Box::new(Item { contents: 42 });
    let r: &'static Item = Box::leak(boxed);
    println!("'static item is {r:?}");
}

ただし、ヒープに確保した値が永遠に解放されないので、メモリリークが起こりうる。それでも、メモリリークは Rust のメモリ安全性には影響しない。

キーワード