// dictionary
静的変数.
静的変数(static item)は、プログラム全体の実行期間にわたって存在する名前付きの値です。staticキーワードで宣言し、定数と同じく型注釈が必須で、初期化に書けるのは定数式だけです。慣習として名前はSCREAMING_SNAKE_CASEで付けます。
定数との最大の違いは、値がメモリ上に唯一の実体として置かれることです。その実体はプログラムが終わるまで生き続けるため、借用すると'staticライフタイムの参照が得られます。
static SHOP_NAME: &str = "プログルストア"; // 型注釈は省略できない
const TAX_RATE: f64 = 0.1;
fn main() {
println!("{SHOP_NAME}の税率: {}%", TAX_RATE * 100.0);
// 何度借用しても同じアドレスを指す
println!("{:p} {:p}", &SHOP_NAME, &SHOP_NAME);
}Playgroundで開く定数との違い
どちらも定数式で初期化する不変の名前付き値ですが、実体の持ち方が異なります。
| 観点 | const |
static |
|---|---|---|
| 実体 | 使用箇所ごとに値が埋め込まれる | プログラム全体で1つ |
| アドレス | 同じアドレスになる保証はない | つねに同じアドレスを指す |
| 可変にできるか | できない | static mutと書ける(unsafeが必要) |
小さな値に名前を付けたいだけならconst、大きなデータを1箇所に置きたい場合やアドレスの同一性が意味を持つ場合はstatic、と使い分けます。
static mutがunsafeな理由
static mutと書くと書き換え可能な静的変数になりますが、読み書きのどちらにもunsafeが必要です。理由は、コンパイラが排他性を検査できないことにあります。静的変数はプログラムのどこからでも名前だけで触れて寿命も尽きないため、「いま誰が書き換えているか」を追跡する相手を特定できません。可変性の「書き換えるなら排他的に」という原則が保証できず、2つのスレッドが同時に書き込めばデータ競合が起きます。それを避ける検証は書き手の責任になります。
static mut COUNTER: i32 = 0;
fn main() {
COUNTER += 1; // エラー: E0133(可変な静的変数の操作にはunsafeが必要)
println!("{}", COUNTER);
}Playgroundで開く補足
Rust 2024エディションでは参照自体が禁止
static mutへの参照(&・&mut)は、共有と可変が同時に成立した瞬間に、そこから読み書きしなくても未定義動作になります。この違反はコンパイラに検出できないため、Rust 2024エディションではstatic_mut_refsリントがdenyになり、unsafeブロックの中であってもコンパイルエラーです。新規のコードでstatic mutを使う理由は実質ありません。
書き換えたい場合の代替手段
標準ライブラリには、staticのまま安全に書き換えるための型が用意されています。カウンタならAtomicUsize、複雑なデータならMutex、初期化を遅らせたいならOnceLock・LazyLockが使えます。いずれも内部可変性を持つ型で、&'static Tのまま安全に内部状態を扱えます(OnceLock・LazyLockは変更ではなく初期化のみ)。
use std::sync::atomic::{AtomicUsize, Ordering};
static VISITOR_COUNT: AtomicUsize = AtomicUsize::new(0);
fn main() {
VISITOR_COUNT.fetch_add(1, Ordering::Relaxed); // unsafe が不要
println!("来店者数: {}人", VISITOR_COUNT.load(Ordering::Relaxed));
}Playgroundで開くSyncが必要な理由
mutの付かない静的変数はどのスレッドからも共有参照で読めるため、型がSyncトレイトを実装している必要があります。Syncでない型(RcやRefCellなど)をstaticに置こうとするとエラー: E0277になります。static mutにはこの制約がありません(そのぶん安全性の保証もありません)。
静的変数はドロップされない
静的変数はプログラム終了時にdropが呼ばれません(The Rust Reference「Static items」の規定)。値そのものはプロセスの終了とともに解放されますが、ファイルを閉じる・バッファを書き出すといった後始末の処理は動かないため、staticにそうした責務を持つ値を置く場合は明示的に処理を呼ぶ必要があります。
この辞書が使われているページ
backlinks 5