Rust Globals Safe static mut with mut_static


Example

Mutable global items (called static mut, highlighting the inherent contradiction involved in their use) are unsafe because it is difficult for the compiler to ensure they are used appropriately.

However, the introduction of mutually exclusive locks around data allows memory-safe mutable globals. This does NOT mean that they are logically safe, though!

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

This code produces the output:

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

This is not something that should happen in Rust normally. foo() did not take a mutable reference to anything, so it should not have mutated anything, and yet it did so. This can lead to very hard to debug logic errors.

On the other hand, this is sometimes exactly what you want. For instance, many game engines require a global cache of images and other resources which is lazily loaded (or uses some other complex loading strategy) - MutStatic is perfect for that purpose.