How can I reset the contents
avnerbarr opened this issue · comments
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);
}
}
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.
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(())
}
@EdmundsEcho for “ // set or get the handle to arc” bit, you can use https://docs.rs/once_cell/latest/once_cell/sync/struct.OnceCell.html#method.get_or_init
@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!