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); }