ことの発端
最近、Rust
に入門しました。
どんな言語も課題があると習得も早いだろうってことで、まずはReactiveX
をRust
で実装してみました。1
ReactiveX
は関数型や非同期を扱うライブラリで、ほぼすべてがジェネリクスで構成されます。
その中で、なかなかスンナリ入ってこなかったのが static
でした。
で、個人的に色々とやってみて、「なるほど」となったので記事にしてみました。
Rust の static
まず、C++
のstatic
とは似て非なるものです。
罪悪感を抱えながら「今日もstatic(グローバルステート)を増やしてしまった。。。」的な「あのstatic
」ではありません。
まず、Rust
の static
には2種類あります。
- staticライフタイム
- staticライフタイム境界
staticライフタイム
は C++
の static
と同じです。
が、しかし、Rust
ではプリミティブ型やリテラルだけが static
定義できます。つまり、オブジェクトは static ライフタイム
が使えません。Singleton
パターンを使ってグローバルステートを実装することはできますが、staticライフタイム
なオブジェクトは作れません。
ほいで、次の staticライフタイム境界
が曲者です。
staticライフタイム境界
とは
ざっくりいうと、
「 参 照 が 含 ま れ な い も の 」
です。
参照は所有者が居てその所有者が手放すまでが生存期間(ライフタイム)になりますが、その参照が含まれていなければ全てstaticライフタイム境界
です。(もうね〜、何というか、言い方変えて欲しい。。。)
具体的にコードで実証してみます。
staticライフタイム境界
を感じる
準備
まず、こんなコードを書きます。
fn is_static<T>(_: T)
where
T: 'static, // <-- これが 「satticライフタイム境界」 というヤツです。
{}
これはstaticライフタイム境界
の変数を渡さないとコンパイルエラーになる関数です。
この関数に色々と値を渡してみると理解が早いかもです。
リテラル
C++er
は、この時点で「??」ですが、これが先述のstaticライフタイム境界
です。
is_static(123);
実体をmove
let x = 0;
is_static(x);
参照
「参照が含まれるもの」というか、参照そのものもNGです。
let x = 0;
is_static(&x);
クロージャー(外部変数のキャプチャなし)
C++
ではラムダ式です。
let f = || {};
is_static(f);
クロージャー(参照でキャプチャ)
クロージャーは外部変数のキャプチャしているオブジェクトです。
このキャプチャしている変数は move
を指定しないと参照になります。
let x = 0;
let f = || println!("{}", x);
is_static(f);
クロージャー(move
でキャプチャ)
move
すればクロージャー内は実体を所有することになるので、無問題。
let x = 0;
let f = move || println!("{}", x);
is_static(f);
'static str&
staticライフタイム
の参照は許されます。
let a = "abc"; // <-- &'static str
is_static(a);
所感
やっぱり static
と書くのは抵抗がある。。w
-
ちょっとだけ本気で作ってみた -> another-rxrust ↩