Search code examples
rustconfigmutability

How do I set values nested within multiple Options if they are not already set?


I have a configuration setup such as this:

#[derive(Debug, Deserialize, Serialize)]
struct Config {
    defaults: Option<Default>,
}

#[derive(Debug, Deserialize, Serialize)]
struct Default {
    duration: Option<Millis>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Millis(u64);

Having a value of let cfg: &mut Config, how could I easily set the duration of this value?

I tried this, which panics if the value is not there to begin with:

*cfg.default.as_mut().unwrap().duration.as_mut().unwrap() = Millis(1234)

I have not found a way around those unwraps to create the values on demand other than this, which is even more verbose...

if cfg.defaults.is_none() {
    cfg.defaults = Some(Default { duration: None });
}

if cfg.defaults.as_mut().unwrap().duration.is_none() {
    cfg.defaults.as_mut().unwrap().duration = Some(Millis(1234));
}

What's "The Way" to do this?


Solution

  • This is what the get_or_insert method is for:

    #[derive(Debug)]
    struct Config {
        defaults: Option<Default>,
    }
    
    #[derive(Debug)]
    struct Default {
        duration: Option<Millis>,
    }
    
    #[derive(Debug)]
    struct Millis(u64);
    
    fn main() {
        let mut config = Config { defaults: None };
    
        config
            .defaults
            .get_or_insert(Default { duration: None })
            .duration
            .get_or_insert(Millis(0))
            .0 = 42;
    
        // Config { defaults: Some(Default { duration: Some(Millis(42)) }) }
        println!("{:?}", config);
    }
    

    (link to playground)