Assigning nested optional structure fields without long chain of `if let Some()`

I have two structures which aren't necessary the same type but have at least 3 levels of the nesting, every field is Option. In case of just accessing the field the chain of map() or and_then() could suffice depending on the details. But it's seems (at least to me, Rust newbie) not possible to work with mutable references this way due to the lack of syntax/methods.

I want to avoid long nested chains like that:

   if let Some(a) = bla.a {
        if let Some(b) = a.b {
            if let Some(c) = b.c {
                if let Some(ref mut a1) = foo.a {
                    if let Some(ref mut b1) = a1.b {
                        if let Some(ref mut c1) = b1.c {
                            // Here we could perform various modifications on "c" member
                            // before the assignment for this operation to make sense
                            *c1 = some_operation(c)
                        }
                    }
                }
            }
        }
    }

Here is the full code: Rust Playground

As of Rust 1.65 you can use let-else to flatten the nesting:

let Some(a) = bla.a else { return; };
let Some(b) = a.b else { return; };
let Some(c) = b.c else { return; };
let Some(ref mut a1) = foo.a else { return; };
let Some(ref mut b1) = a1.b else { return; };
let Some(ref mut c1) = b1.c else { return; };
  
*c1 = c;

You could also use and_then to build the bindings in fewer statements:

let Some(c) = bla.a
    .and_then(|a| a.b)
    .and_then(|b| b.c)
else {
    return;
};
  
let Some(c1) = foo.a.as_mut()
    .and_then(|a| a.b.as_mut())
    .and_then(|b| b.c.as_mut())
else {
    return;
};
  
*c1 = c;

You can reduce some nesting with and_then.

let-chains are expected to stabilize soonish, and with them you don't need to nest at all.

And on nightly, you can use let_chains:

#![feature(let_chains)]

if let Some(a) = bla.a
    && let Some(b) = a.b
    && let Some(c) = b.c
    && let Some(ref mut a1) = foo.a
    && let Some(ref mut b1) = a1.b
    && let Some(ref mut c1) = b1.c
{
    // Here we could perform various modifications on member
    // before the assignment for this operation to make sense
    *c1 = c
}
1 Like

Another option is matching both structs within a tuple:

match (bla.a, &mut foo.a) {
    (
        Some(Lv2 { b: Some(Lv3 { c: Some(c) }) }),
        Some(Lv2 { b: Some(Lv3 { c: Some(c1) }) }),
    ) => *c1 = c,
    _ => (),
}
4 Likes

Thanks everyone, from my point of view this tuple matching looks best from being concise and easily understood deconstruction.

You could also have a look at the if_chain crate: if_chain - Rust

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.