使用 mut static 的安全静态 mut

可变全局项(称为 static mut,突出显示其使用中涉及的固有矛盾)是不安全的,因为编译器很难确保它们被恰当地使用。

但是,在数据周围引入互斥锁允许内存安全的可变全局变量。但这并不意味着它们在逻辑上是安全的!

#[macro_use]
extern crate lazy_static;
extern crate mut_static;

use mut_static::MutStatic;

pub struct MyStruct { value: usize }

impl MyStruct {
    pub fn new(v: usize) -> Self{
        MyStruct { value: v }
    }
    pub fn getvalue(&self) -> usize { self.value }
    pub fn setvalue(&mut self, v: usize) { self.value = v }
}

lazy_static! {
    static ref MY_GLOBAL_STATE: MutStatic<MyStruct> = MutStatic::new();
}

fn main() {
    // Here, I call .set on the MutStatic to put data inside it.
    // This can fail.
    MY_GLOBAL_STATE.set(MyStruct::new(0)).unwrap();
    {
        // Using the global state immutably is easy...
        println!("Before mut: {}", 
                 MY_GLOBAL_STATE.read().unwrap().getvalue());
    }
    {
         // Using it mutably is too...
         let mut mut_handle = MY_GLOBAL_STATE.write().unwrap();
         mut_handle.setvalue(3);
         println!("Changed value to 3.");
    } 
    {
        // As long as there's a scope change we can get the 
        // immutable version again...
        println!("After mut: {}", 
                 MY_GLOBAL_STATE.read().unwrap().getvalue());
    }
    {
        // But beware! Anything can change global state!
        foo();
        println!("After foo: {}", 
                 MY_GLOBAL_STATE.read().unwrap().getvalue());
    }
 
}

// Note that foo takes no parameters
fn foo() {
    let val;
    {
        val = MY_GLOBAL_STATE.read().unwrap().getvalue();
    }
    {
        let mut mut_handle = 
            MY_GLOBAL_STATE.write().unwrap();
        mut_handle.setvalue(val + 1);
    }
}

此代码生成输出:

Before mut: 0
Changed value to 3.
After mut: 3
After foo: 4

这通常不会发生在 Rust 中。foo() 并没有对任何东西进行可变引用,因此它不应该突变任何东西,但它确实如此。这可能导致很难调试逻辑错误。

另一方面,这有时正是你想要的。例如,许多游戏引擎需要全局缓存图像和其他懒惰加载的资源(或使用其他复杂的加载策略) - MutStatic 非常适合此目的。