1

In my Rust project, I need a globally hold, static array or vec that is initialized once where modules can register values or functions on. I thought, this would be possible using the lazy_static!-crate, but it doesn't seem so.

This is what I want to achieve:

  • Module a initializes an array/vec with some data.
  • Module b (or further modules) then extend this array/vec to further data.
  • All this should only be done once at program startup, and the array won't be modified during the program's execution. It is just a lookup-table, globally hold, but once created from several modules.

This is my first draft, which does not work playground link

mod a
{
    use lazy_static::lazy_static; // 1.4.0

    lazy_static!{
        #[derive(Debug)]
        pub static ref TEST: Vec<u32> = vec![1, 2, 3];
    }
}

mod b  // when module b is removed, it works. 
{
    use lazy_static::lazy_static; // 1.4.0
    use crate::a::TEST;

    lazy_static!{
        TEST.extend(vec![4, 5, 6]);
    }
}

use a::TEST;

fn main() {
    for i in 0..TEST.len() {
        println!("{}", TEST[i]);
    }
}

Can anybody help?

glasflügel
  • 325
  • 2
  • 11
  • 1
    You might also be interested in `linkme` - https://docs.rs/linkme/0.2.6/linkme/struct.DistributedSlice.html – Cerberus Jul 17 '21 at 11:48

1 Answers1

3

A couple things to note:

  • as far as I know, lazy_static! is for declaring static variables, so mod b can't use the macro just to mutate other statics, like you are trying in your example
  • for statics to be mutable in Rust, you need to wrap them in a Mutex to follow Rust's whole thing of guaranteeing thread safety
  • modules are designed to collect things like structs, functions, traits, etc., so if you want them to 'do' something, you need to have it in a function call.

All that being said, I hope this snippet can help you out.

Rust Playground

mod a {
    use lazy_static::lazy_static; // 1.4.0
    use std::sync::Mutex;

    lazy_static!{
        #[derive(Debug)]
        pub static ref TEST: Mutex<Vec<u32>> = Mutex::new(vec![1, 2, 3]);
    }
}

mod b {
    use crate::a::TEST;
    
    pub fn append_to_test() {
        TEST.lock().unwrap().extend(vec![4, 5, 6]);
    }
}

use a::TEST;

fn main() {
    crate::b::append_to_test();
    
    println!("{:?}", TEST.lock().unwrap());
}
Jeremy Meadows
  • 2,314
  • 1
  • 6
  • 22
  • Thank you, this is exactly the solution I was looking for! You're right, I read about mutexes some time ago but didn't remember about it in detail. – glasflügel Jul 17 '21 at 09:05
  • Is there any way to call crate::b::append_to_test() automatically BEFORE the main() or any tests of the crate are being executed? Here's another playground for this case (renamed some functions): https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=db647ea6904c0008e09c58bb85d55e50 – glasflügel Jul 19 '21 at 10:46
  • Technically no, you cannot execute code before `main()`, as that is the entry point to the program. If you just want it to be logically separated, however, you can use a [SyncLazy](https://doc.rust-lang.org/std/lazy/struct.SyncLazy.html) if you are running Rust Nightly. This is definitely not the most elegant way of writing it, as I've just shoehorned some code into your example, but [this playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c451f9d478d5a3a0343580f4b5683217) creates a global static `SyncLazy` which wraps `a::ITEMS`, as well as calls the methods. – Jeremy Meadows Jul 20 '21 at 04:06