rustrust-compiler-plugin

Is there a clean way to have a global mutable state in a Rust plugin?


The only way I have found to do this is with an unsafe singleton function:

fn singleton() -> &'static mut HashSet<String> {
    static mut hash_map: *mut HashSet<String> = 0 as *mut HashSet<String>;

    let map: HashSet<String> = HashSet::new();
    unsafe {
        if hash_map == 0 as *mut HashSet<String> {
            hash_map = mem::transmute(Box::new(map));
        }
        &mut *hash_map
    }
}

Is there a better way? Perhaps we could do something in the plugin_registrar function?

By global mutable state, I mean a mutable variable that can be used by multiple procedural macros and/or attributes.

Update:

I am writing a compiler plugin to provide a sql! procedural macro. I have a #[sql_table] attribute to be used on structs so that I can get the name and columns of the SQL table.

I need global mutable state to save the name and fields of the struct in the attribute, so that the procedural macro can check that all the identifiers exist.


Solution

  • The lazy_static! macro can help with having a global would initializer is not static. https://crates.io/crates/lazy_static/ It does something similar to your if hash_map == 0 as *mut HashSet<String>, but takes care of the data race in case more than one thread tries to do this at the same time.

    As to mutability, to avoid more data races you’ll have to protect it somehow, probably with a Mutex.

    All together:

    #[macro_use] extern crate lazy_static;
    use std::sync::Mutex;
    use std::collections::HashSet;
    
    lazy_static! {
        static ref THINGS: Mutex<HashSet<String>> = Mutex::new(HashSet::new());
    }
    
    fn usage() {
        // unwrap only fails if the lock is poisoned:
        // if another thread panicked while holding the lock.
        THINGS.lock().unwrap().insert("thing".to_owned())
    }