Why does this fail borrow check?


#1

Hi, I’m having trouble with some code, and I’ve managed to reduce the issue to this test case:

fn test<'a>(xs: &'a mut Vec<String>) -> Option<&'a String> {
    if let Some(x) = xs.first_mut() {
        return Some(x);
    }

    xs.first()
}

fn main() {
    test(&mut vec!["hi".to_string()]);
}

Compiler output:

t.rs:6:5: 6:7 error: cannot borrow `*xs` as immutable because it is also borrowed as mutable [E0502]
t.rs:6     xs.first()
           ^~
t.rs:2:22: 2:24 note: previous borrow of `*xs` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*xs` until the borrow ends
t.rs:2     if let Some(x) = xs.first_mut() {
                            ^~
t.rs:7:2: 7:2 note: previous borrow ends here
t.rs:1 fn test<'a>(xs: &'a mut Vec<String>) -> Option<&'a String> {
       ...
t.rs:7 }
       ^
t.rs:6:5: 6:7 error: cannot borrow `*xs` as immutable because it is also borrowed as mutable [E0502]
t.rs:6     xs.first()
           ^~
t.rs:2:22: 2:24 note: previous borrow of `*xs` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*xs` until the borrow ends
t.rs:2     if let Some(x) = xs.first_mut() {
                            ^~
t.rs:7:2: 7:2 note: previous borrow ends here
t.rs:1 fn test<'a>(xs: &'a mut Vec<String>) -> Option<&'a String> {
       ...
t.rs:7 }
       ^
error: aborting due to 2 previous errors

So, if a branch (if let or match) borrows a mutable reference to a variable and uses it to return early, the borrow is considered alive for the rest of the function body, and I can’t find a way to reduce this scope. I don’t see why this happens, especially considering that when there’s no early return the code compiles…

Thanks in advance for the help!


#2

I think this is similar to the problems described in the non lexical lifetimes blog post series (Problem #3 especially).

A possible workaround would be

fn test<'a>(xs: &'a mut Vec<String>) -> Option<&'a String> {
    if xs.first_mut().is_some() {
        let x = xs.first_mut().unwrap();
        Some(x)
    } else {
        xs.first()
    }

}

fn main() {
    test(&mut vec!["hi".to_string()]);
}

#3

Indeed! I believe this is problem #3 in the post, and my case matches the example get_default1.

As for your suggestion, unfortunately it’s not that simple in my actual code, as the control flow is actually quite a bit more complicated :frowning: I do have a working alternative, but it’s pretty ugly.


#4

Can you give a link to the actual code? Perhaps a less ugly workaround is possible…