How to unwrap data safely and concisely?

When I have wrapped data like Some(Some(val)) how can I safely unwrap the value?
I thought about using match and if let but both ends up being redundant, and unwrap() can cause panic.

What is the best way to unwrap wrapped data concisely (ideally online like JS optional chaining) and safely?

use std::collections::HashMap;

fn main() {
    let a = Some(Some(Some(Some(HashMap::from([(
        "test".to_owned(),
        Some("test".to_owned()),
    )])))));
    
    // Very redundant
    if let Some(a) = a {
        if let Some(a) = a {
            if let Some(a) = a {
                if let Some(a) = a {
                    let val = a["test"].clone();
                    if let Some(_val) = val {
                        // something with val
                        todo!();
                    }
                }
            }
        }
    }
}

Are you looking for Option::flatten()?

5 Likes
if let Some(Some(Some(Some(map)))) = a {
    let val = map["test"].clone();
    if let Some(_val) = val {
        // something with val
        todo!();
    }
}
7 Likes

You can also use the ? operator, but it's a little awkward right now because try blocks aren't stable, and you'll need to use a closure instead:

    let val: Option<_> = (|| a????["test"].clone())();
    
    // do something with val
    if let Some(v) = val {
        dbg!(v);
    }
1 Like

Yes that's what im looking for! thanks

this is also cool! thanks

How to handle nested optional object type?

{
  "parent": Option<{"sub": { "child": Option<String> }}>
}

In this case, I don't think flatten() or Some(Some()) don't work, right?

I end up writing something like the following

if let Some(sub) = parent {
    if let Some(text) = sub.child {
        text
       todo!();
    }
}

Technically, you can deconstruct the structure in pattern, like you do with the enum (e.g. Option) - playground:

if let Some(Parent { child: Some(s) }) = arg {
    // do something
}

If the struct is more complex, your approach with nested if lets might end up more readable though.

3 Likes

This is also where the ? operator starts being more interesting:

fn foo(arg: Option<Parent>)->Option<String> {
    let child = arg?.child?;
    println!("Found child: {}", child);
    Some(child)
}
3 Likes

Thanks!

Thanks