Mutably borrowed in previous iteration despite no chance of conflicting borrow

I ran into some rustc behavior that doesn't seem right. Posting here first to understand if it counts as a bug or a problem with my code.

The code below passes a mutable reference to a function call (try_make_foo), repeatedly in a loop. That function returns a struct that holds on to the mutable reference.

struct Foo<'a> {
    x: &'a mut i32,
}

impl<'a> Foo<'a> {
    fn new(x: &'a mut i32) -> Self {
        Self { x }
    }
}

fn try_make_foo<'a, 'b: 'a>(x: &'b mut i32) -> Option<Foo<'a>> {
    Some(Foo::new(x))
}

fn get_foo<'a, 'b: 'a>(x: &'b mut i32) -> Foo<'a> {
    loop {
        let foo = match try_make_foo(x) {
            Some(foo) => foo,
            None => continue,
        };

        break foo;
    }
}

fn main() {
    let mut x = 42;
    let foo = get_foo(&mut x);
    *foo.x += 1;
    x += 1;
    assert_eq!(x, 44);
}

Even though the loop always ends after the function yields the value, rustc gives the error:

error[E0499]: cannot borrow `*x` as mutable more than once at a time
  --> borrowinto.rs:17:38
   |
15 | fn get_foo<'a, 'b: 'a>(x: &'b mut i32) -> Foo<'a> {
   |            -- lifetime `'a` defined here
16 |     loop {
17 |         let foo = match try_make_foo(x) {
   |                                      ^ `*x` was mutably borrowed here in the previous iteration of the loop
...
23 |         break foo;
   |               --- returning this value requires that `*x` is borrowed for `'a`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0499`.

The loop is necessary to get this error. Removing the loop or changing continue to panic! will cause compilation to succeed.

As a workaround, I found that it's possible for the function taking the mutable reference to return it back to the caller in the failure case, so that it can be reused in the next iteration. But to me it feels like that shouldn't be necessary.

This is a limitation of the current borrow-checker, but nightly rustc -Zpolonius accepts it.

2 Likes

Here's the issue.

1 Like

Thanks for the quick replies folks! Good to know it is a goal of rustc to eventually support this. Confirmed that it does work on nightly with polonius.