3

I have a config that I want initialized on startup, but not have to re-read the file every time. To do this, I'm using lazy_static:

lazy_static! {
  static ref SETTINGS: Settings = {
    match Settings::init() {
      Ok(c) => c,
      Err(e) => panic!("{}", e),
    }
  };
}

But now I have a method that updates that config file, and I want to "re-initialize" / update it, without having to re-start the program.

  pub fn save_config_file(data: &str) -> Result<String, Error> {
    fs::write(CONFIG_FILE, data)?;
    SETTINGS = {
      match Settings::init() {
        Ok(c) => c, // The line with the error
        Err(e) => panic!("{}", e),
      }
    };

    Self::read_config_file()
  }

Which gives me the error: [rustc E0308] [E] mismatched types expected struct settings::SETTINGS, found struct settings::Settings

Is there any way to re-initialize a lazy_static? Or do I have to restart the program?

dessalines
  • 6,352
  • 5
  • 42
  • 59
  • You can use a RefCell to change the contents of a lazy static. – PiRocks Apr 11 '20 at 16:44
  • Is this documented anywhere, how RefCell works with lazy_static? – dessalines Apr 11 '20 at 16:44
  • RefCell works the same anywhere. It allows you to mutate its contents, even if you only have a immutable reference to the RefCell. – PiRocks Apr 11 '20 at 16:47
  • It's worth mentioning that you probably shouldn't be using lazy satic to hold settings, and should instead initialize the settings in your main function/early function, and then pass references around, – PiRocks Apr 11 '20 at 16:48
  • Does this answer your question? [How do I create a global, mutable singleton?](https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton) – trent Apr 11 '20 at 21:55
  • 1
    (@PiRocks Note that because statics must be thread safe, you need to use `Mutex`, `RwLock` or atomics -- `RefCell` would work with a `thread_local` though.) – trent Apr 11 '20 at 21:57

1 Answers1

4

Okay, figured this one out. Mainly from here: How do I assign a String to a mutable static variable?

Add derive Clone to your struct:

#[derive(Debug, Deserialize, Clone)]

Wrap your lazy_static in an RwLock, initialize it normally.

use std::sync::RwLock;

lazy_static! {
  static ref SETTINGS: RwLock<Settings> = RwLock::new(
    match Settings::init() {
      Ok(c) => c,
      Err(e) => panic!("{}", e),
    }
  );
}

To read, you can do this:

  pub fn get() -> Self {
    SETTINGS.read().unwrap().to_owned()
  }

To write:

    let mut new_settings = SETTINGS.write().unwrap();
    *new_settings = match Settings::init() {
      Ok(c) => c,
      Err(e) => panic!("{}", e),
    };
dessalines
  • 6,352
  • 5
  • 42
  • 59