6

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
antoyo
  • 11,097
  • 7
  • 51
  • 82
  • 3
    Do you mean a [plugin for the Rust compiler](http://doc.rust-lang.org/stable/book/compiler-plugins.html), like a syntax extension? – Shepmaster Sep 13 '15 at 23:48
  • Yes, I mean a plugin for the Rust compiler. – antoyo Sep 14 '15 at 00:09
  • Note that your example of a singleton is *broken* for parallel access. There are other questions ([1](http://stackoverflow.com/questions/27747785/), [2](http://stackoverflow.com/questions/27791532), [3](http://stackoverflow.com/questions/27221504)) that should help you create one correctly. I believe (but am not sure) that parallel access is highly likely in the compiler. – Shepmaster Sep 14 '15 at 00:17

1 Answers1

5

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())
}
Simon Sapin
  • 9,790
  • 3
  • 35
  • 44