スタティックライフタイム
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
型を持つ変数を作成します。
では、それぞれの方法の例を見ていきましょう。
// `'static`ライフタイムを持つ定数を作成。 static NUM: i32 = 18; // `NUM`への参照を返します。ライフタイムは`'static`から引数の // ライフタイムへと圧縮されています。 fn coerce_static<'a>(_: &'a i32) -> &'a i32 { &NUM } fn main() { { // 文字列リテラルを用いて変数を作成し、出力します。 let static_string = "I'm in read-only memory"; println!("static_string: {}", static_string); // `static_string`がスコープから抜けると、参照は使用することが // できなくなりますが、データはバイナリ中に残ります。 } { // `coerce_static`関数を呼び出すために、整数を作成。 let lifetime_num = 9; // `NUM`を`lifetime_num`のライフタイムへと圧縮。 let coerced_static = coerce_static(&lifetime_num); println!("coerced_static: {}", coerced_static); } println!("NUM: {} stays accessible!", NUM); }
Since 'static
references only need to be valid for the remainder of a program's life, they can be created while the program is executed. Just to demonstrate, the below example uses Box::leak
to dynamically create 'static
references. In that case it definitely doesn't live for the entire duration, but only for the leaking point onward.
extern crate rand; use rand::Fill; fn random_vec() -> &'static [usize; 100] { let mut rng = rand::thread_rng(); let mut boxed = Box::new([0; 100]); boxed.try_fill(&mut rng).unwrap(); Box::leak(boxed) } fn main() { let first: &'static [usize; 100] = random_vec(); let second: &'static [usize; 100] = random_vec(); assert_ne!(first, second) }
トレイト境界
トレイト境界としての'static
は型が非静的な参照を含まないことを意味します。言い換えると、レシーバはその型をいくらでも長く保持することができ、意図的にドロップするまでは決して無効になることはないということです。
次のポイントを押さえておきましょう。所有権のある値が'static
ライフタイム境界をパスするとしても、その値への参照が'static
ライフタイム境界をパスするとは限りません。
use std::fmt::Debug; fn print_it( input: impl Debug + 'static ) { println!( "'static value passed in is: {:?}", input ); } fn main() { // i は所有されていて、かつ参照を含まないので 'static。 let i = 5; print_it(i); // おっと、&i は main() のスコープで定義された // ライフタイムしかもたないため 'static ではありません。 print_it(&i); }
コンパイラのメッセージはこのようになります。
error[E0597]: `i` does not live long enough
--> src/lib.rs:15:15
|
15 | print_it(&i);
| ---------^^--
| | |
| | borrowed value does not live long enough
| argument requires that `i` is borrowed for `'static`
16 | }
| - `i` dropped here while still borrowed