Once構造体は、アプリケーション内で一度だけ初期化したい場合に使う構造体。
(Javaの静的初期化子static{ }に相当)
use std::sync::Once;
MyConfigという構造体を一度だけ初期化する例。
use std::sync::Once;
struct MyConfig {
value: i32,
}
impl MyConfig {
fn new() -> MyConfig {
MyConfig { value: 123 }
}
}
static INIT: Once = Once::new();
static mut MY_CONFIG: Option<MyConfig> = None;
fn get_my_config() -> &'static MyConfig {
unsafe {
INIT.call_once(|| MY_CONFIG = Some(MyConfig::new()));
MY_CONFIG.as_ref().unwrap()
}
}
fn main() {
let my_config = get_my_config();
println!("{}", my_config.value);
}
まず、OnceのインスタンスをOnce::new()で生成する。
生成したOnce(INIT)に対してcall_once()を呼ぶと、そこで指定したクロージャーが一度だけ呼ばれるので、この中で自分のデータを初期化(生成)する。
初期化対象(MY_CONFIG)は可変(mut)なstatic変数なので、使用する際はunsafeで囲む必要がある。
実のところ、上記の例だと、Onceを使わなくてもstaticな変数としてMyConfigを保持しておくことが出来る。
struct MyConfig {
value: i32,
}
static MY_CONFIG: MyConfig = MyConfig{ value:123 };
fn main() {
println!("{}", MY_CONFIG.value);
}
しかし、staticでの初期値は固定値にならなくてはならず、関数を呼び出すことは出来ない。
struct MyConfig {
value: i32,
}
impl MyConfig {
fn new() -> MyConfig {
MyConfig { value: 123 }
}
}
static MY_CONFIG: MyConfig = MyConfig::new(); // コンパイルエラー
関数を呼び出して初期化を行いたい場合(初期値を決めるために何らかの処理が必要な場合)はOnceを使うと良い。
ちなみに、MyConfigの中身を後から変更したいなら、get_my_config()の返り型をmutにする。
fn get_my_config() -> &'static mut MyConfig { unsafe { INIT.call_once(|| MY_CONFIG = Some(MyConfig::new())); MY_CONFIG.as_mut().unwrap() } }
fn main() {
let my_config = get_my_config();
my_config.value = 111;
println!("{}", my_config.value);
}