Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing data into a module-level mutable static variable

I have a set of functions within a module that need access to some shared initialization-time state. Effectively I'd like to model this with a static mutable vector like:

static mut defs: Vec<String> = vec![];

fn initialize() {
    defs.push("One".to_string());
    defs.push("Two".to_string()); 
}

(Example: http://is.gd/TyNQVv, fails with "mutable statics are not allowed to have destructors".)

My question is similar to Is it possible to use global variables in Rust?, but uses a Vec (i.e. a type with destructor), so the Option-based solution to that question doesn't seem to apply. Namely, this fails with the same error as my first attempt:

static mut defs: Option<Vec<String>> = None;

fn initialize() {
    let init_defs = vec![];
    init_defs.push("One".to_string());
    init_defs.push("Two".to_string()); 
    defs = Some(init_defs);
}
  1. Is there a way to get access to a static ("global") vector that is populated at initialization time and visible at runtime?

  2. Are there other patterns I should be considering to support this use case? Passing explicit references to the state vector is possible, but would clutter up a very large number of function signatures that all need access to this state.

like image 982
Bosh Avatar asked Mar 03 '15 05:03

Bosh


1 Answers

You can use lazy_static for this purpose:

lazy_static! {
    static ref defs: Vec<String> = {
        let mut init = vec!["One".to_string(), "Two".to_string()];
        // init.push(...); etc. etc.
        init
    }
}

That initialises a vector on the first access, and it is immutable after that. If you wish to modify it later, wrapping it in a std::sync::Mutex is a good first step.

Are there other patterns I should be considering to support this use case? Passing explicit references to the state vector is possible, but would clutter up a very large number of function signatures that all need access to this state.

One pattern to consider is creating a context object that stores all the info the functions need, e.g.

struct Context {
    defs: Vec<String>
}

and then passing around Context ensures everyone knows what they need to know. You can even consider putting all/many/some of the functions as methods on Context, e.g.

impl Context {
    fn foo(&self) {
        if self.defs.len() > 10 {
             println!("lots of defs");
        }
    }
    // ...
}

This pattern is especially good if you need to modify the context (automatically ensures thread safety), and/or if you wish to have several independent instances in a single process.

like image 136
huon Avatar answered Sep 24 '22 03:09

huon