matklad / once_cell

Rust library for single assignment cells and lazy statics without macros

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How can I reset the contents

avnerbarr opened this issue · comments

commented

I have a function for creating the instance and initialising the cell, but in some cases, for instance under testing , i need to reset the cell to a new value.

Here is an example:

struct Configuration {
  // many more fields but omitted. This object gets created in the "get()" method which loads the cell
  name : String
  count : i32
}

static INSTANCE: OnceCell<Configuration> = OnceCell::new();

impl Configuration {
  pub fn get() -> &'static Configuration {
    Instance.get_or_init(|| {
      Configuration {
                    name: "blah".to_string(),
                    count: 0
      }
    })

  pub fn reset(config: Configuration) {
// how do I reset it? seems this fails if the cell had already been initialized
/// Sets the contents of this cell to `value`.
        ///
        /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was
        /// full.
      INSTANCE.set(config);
  }
}
commented

so what would I need to do for that to work?

I'd change the design to not rely on global static instance of Config. Otherwise, something like OnceCell<Mutex<Config>> or OnceCell<ArcSwap<Config>> should work.

https://docs.rs/arc-swap/0.4.7/arc_swap/

At some level I can see how the request here is a contradiction to the design of once_cell. However, it is a feature that would be great to have: reloading the config. Perhaps there is a version of the once_cell that might have the extra accounting of shared refs required to do so. Likely a related idea, the feature would require encapsulating the shared use of the configuration in the memory that is being "reinitialized".

Update

Per the suggestion, I went with:

// global ref to static value
pub static CFG: OnceCell<ArcSwap<Settings>> = OnceCell::new();

// getter and setters
pub fn config_get() -> Result<Guard<Arc<Settings>>, AppError> {
    let cfg = CFG
        .get()
        .ok_or_else(|| AppError::ConfigError("Config was not initialized".to_string().into()))?
        .load();
    Ok(cfg)
}
pub fn config_init() -> Result<(), AppError> {
    // instantiate the configuration
    let new_cfg = Settings::new().map_err(|e| AppError::ConfigError(e.to_string().into()))?;
    // set or get the handle to arc
    if CFG.get().is_none() {
        CFG.set(ArcSwap::from_pointee(new_cfg)).unwrap();
    } else {
        CFG.get().unwrap().store(Arc::new(new_cfg));
    }
    Ok(())
}

@matklad -- Thank you for that suggestion; I'll update it accordingly. FYI, the over-all solution was worth the extra effort. Being able to reload axum (in my case) to read the config files by hitting a reload endpoint greatly improves my workflow. Thank you! - E

Thanks, @EdmundsEcho, I have the exact use-case, and your solution seems to be working fine. Much appreciated!