Skip to content

Misleading/unhelpful error message from borrow checker #55307

@vsajip

Description

@vsajip

The following program, when compiled, gives rise to a misleading and unhelpful error message:

use std::io;

enum Error<'a> {
    Invalid(&'a [u8]),
    Io(io::Error),
}

struct Thing {
    counter: usize
}

enum Result<'a> {
    Done,
    Error(Error<'a>),
    Value(usize)
}

impl Thing {
    fn new() -> Self {
        Self { counter: 0 }
    }

    fn process(&mut self) -> Result {
        self.counter += 1;
        Result::Done // for now
    }
}

fn main() {
    let mut thing = Thing::new();
    loop {
        let result = thing.process();
        match result {
            Result::Done => {
                break;
            }
            Result::Error(e) => panic!(e),
            Result::Value(v) => {
                println!("{}", v);
            }
        }
    }
}

The message is:

   Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `thing` as mutable more than once at a time
  --> src/lib.rs:32:22
   |
32 |         let result = thing.process();
   |                      ^^^^^ mutable borrow starts here in previous iteration of loop
   |
   = note: borrowed value must be valid for the static lifetime...

error: aborting due to previous error

However, the mutable borrow complaint appears remote from the true reason for the error.

The key to determining the problem, as analysed by Daniel Keep here, is the exact language of the error message:

note: borrowed value must be valid for the static lifetime...

At first glance, there are no static borrows anywhere in the code. But what's actually happening is that when there's the panic!(e), e is being used as the literal panic payload. In order for that to work, e itself must live forever. But, as Error contains a borrow, that borrow must live forever.

But the 'a lifetime of that Error borrow is tied to the 'a lifetime in Result. And that lifetime is tied to the lifetime of self, which is in turn the required lifetime of the thing variable.

So thing has to be 'static, but it cannot be 'static because it’s on the stack.

It's not immediately obvious how best to improve the error message, but the current message isn't helpful, because it points to the borrow in the loop.

Apparently Polonius doesn't give any better message.

Links which may be useful:

The sample in Rust Playground
Discussion on users.rust-lang.org

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-lifetimesArea: Lifetimes / regionsC-enhancementCategory: An issue proposing an enhancement or a PR with one.D-confusingDiagnostics: Confusing error or lint that should be reworked.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions