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);
}
Is there a way to get access to a static ("global") vector that is populated at initialization time and visible at runtime?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With