S-JIS[2024-10-14] 変更履歴

Rust Once構造体

Ruststd::sync::Onceのメモ。


概要

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

stdへ戻る / Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま