Conditional `return` seems to hold the reference forever

Hi, thanks for the language! However, I am facing a compilation error and cannot use it :frowning:

I tried this code:

fn main() {}

struct S {
    field: String,
}

impl S {
    fn f(&mut self) -> &str {
        let a = &mut self.field;

        if false { // `false`, or `true`, or whatever condition
            return a;
        }
        // drop(a); // You can add this, but still error

        return &self.field;
    }
}

P.S. The real world case requires mut (though this simple demo does not).

I expected to see this happen: explanation
No error.

IMHO, there are two cases:
If the if gets true, it will immediately return, and everything is OK
If the if gets false, the variable a is never used afterwards. You can also add that drop (but still error). Thus, it should not have problem.

Instead, this happened: explanation

error[E0502]: cannot borrow `self.field` as immutable because it is also borrowed as mutable
  --> src/main.rs:15:16
   |
8  |     fn f(&mut self) -> &str {
   |          - let's call the lifetime of this reference `'1`
9  |         let a = &mut self.field;
   |                 --------------- mutable borrow occurs here
10 |         if true {
11 |             return a;
   |                    - returning this value requires that `self.field` is borrowed for `'1`
...
15 |         return &self.field;
   |                ^^^^^^^^^^^ immutable borrow occurs here


Meta

rustc --version --verbose:

rustc 1.79.0-nightly (805813650 2024-03-31)
binary: rustc
commit-hash: 805813650248c1a2f6f271460d728d1bb852d2a7
commit-date: 2024-03-31
host: x86_64-apple-darwin
release: 1.79.0-nightly
LLVM version: 18.1.2
Backtrace

<backtrace>

P.S. More (roughly equivalent) examples that do not work

fn main() {}

struct S {
    field: String,
}

fn f(arg: &mut S) -> &mut str {
    let a = &mut arg.field;
    if true {
        return a;
    }
    drop(a);

    return &mut arg.field;
}

P.S. Another maybe related example that does not work

fn main() {}

struct S {
    field: String,
}

fn f(arg: &mut S) -> &mut str {
    let a = &mut arg.field;
    let b = if true {
        a
    } else {
        drop(a);
        &mut arg.field
    };
    b
}

gives

error[E0499]: cannot borrow `arg.field` as mutable more than once at a time
  --> src/main.rs:13:9
   |
7  | fn f(arg: &mut S) -> &mut str {
   |           - let's call the lifetime of this reference `'1`
8  |     let a = &mut arg.field;
   |             -------------- first mutable borrow occurs here
...
13 |         &mut arg.field
   |         ^^^^^^^^^^^^^^ second mutable borrow occurs here
14 |     };
15 |     b
   |     - returning this value requires that `arg.field` is borrowed for `'1`

RUSTFLAGS=-Zpolonius cargo r or polonius_the_crab - Rust

2 Likes

To elaborate slightly on the previous reply - this is a current limitation of the borrow checker (there's an issue tracking it here).

Polonius is a new version of the borrow checker that has been under development for a long time, and one of the primary goals of it is to enable code like this to work.

As of the last blog update, the plan was to try to get this to stable by the 2024 edition of Rust, but whether that's still on track, I'm not sure.

2 Likes

Thank you very much for the replies!