E0502 when borrowing in if let

i've minified my code down to this:

fn get(x: &mut Option<u128>) -> &u128 {
    if let Some(xx) = &*x {
        return xx
    }
    x.insert(12);
    &12
}

i'm getting this error:

error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
 --> src/lib.rs:5:5
  |
1 | fn get(x: &mut Option<u128>) -> &u128 {
  |           - let's call the lifetime of this reference `'1`
2 |     if let Some(xx) = &*x {
  |                       --- immutable borrow occurs here
3 |         return xx
  |                -- returning this value requires that `*x` is borrowed for `'1`
4 |     }
5 |     x.insert(12);
  |     ^^^^^^^^^^^^ mutable borrow occurs here

shouldn't the reborrow die after the if statement?

workaround:

fn get(x: &mut Option<u128>) -> &u128 {
    if x.is_some() {
        return x.as_ref().unwrap()
    }
    x.insert(12);
    &12
}

removing the reborrow also works, but in the original code, i'm borrowing a struct field, so that's not an option.

This is a known false positive with the current borrow checker.

if let acts like a match and this is good ol' Problem Case #3, conditional early returns of a borrow.

4 Likes

If the condition can be expressed entirely as pattern matching, then using a ref pattern instead of & lets you defer the reborrow until after the pattern has been determined to successfully match:

fn get(x: &mut Option<u128>) -> &u128 {
    if let Some(ref xx) = x {
        xx
    } else {
        x.insert(12)
    }
}

(Other changes just to tidy up the code, not because they are needed)

4 Likes