Skip to content

Add error help to "cannot borrow ... as mutable more than once at a time" error that recommends assigning to a variable first #77834

Closed
@camelid

Description

@camelid

A newcomer to Rust may have some code that looks like this (playground):

struct Foo;

impl Foo {
  fn foo(&mut self, _: f32) -> i32 { todo!() }
  fn bar(&mut self) -> f32 { todo!() }
  fn baz(&mut self) {
    self.foo(self.bar());
  }
}

Which you would think would work, since the call to self.foo() happens after self.bar() returns, and so they have unique access to self. However, this desugars into something like this:

struct Foo;

impl Foo {
  fn foo(&mut self, _: f32) -> i32 { todo!() }
  fn bar(&mut self) -> f32 { todo!() }
  fn baz(&mut self) {
    Self::foo(&mut self, Self::bar(&mut self));
  }
}

Which, due to something else in the Rust aliasing rules that can be confusing, means that there are actually two unique borrows of self live at the same time, and produces this error:

   Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `*self` as mutable more than once at a time
 --> src/lib.rs:7:14
  |
7 |     self.foo(self.bar());
  |     ---- --- ^^^^ second mutable borrow occurs here
  |     |    |
  |     |    first borrow later used by call
  |     first mutable borrow occurs here

However, this error is confusing because the error is only visible when the code is desugared. Because of how the code is desugared, this code – which is functionally equivalent – compiles fine (playground):

struct Foo;

impl Foo {
  fn foo(&mut self, _: f32) -> i32 { todo!() }
  fn bar(&mut self) -> f32 { todo!() }
  fn baz(&mut self) {
    let temp = self.bar();
    self.foo(temp);
  }
}

Therefore I propose adding a help message to the error that suggests extracting the result into a temporary. Something like this:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
 --> src/lib.rs:7:14
  |
7 |     self.foo(self.bar());
  |     ---- --- ^^^^ second mutable borrow occurs here
  |     |    |
  |     |    first borrow later used by call
  |     first mutable borrow occurs here
  |
  = help: try assigning `self.bar()` to a variable and then call `self.foo()` with that variable

Cc @jyn514

Metadata

Metadata

Assignees

Labels

A-borrow-checkerArea: The borrow checkerA-diagnosticsArea: Messages for errors, warnings, and lintsC-enhancementCategory: An issue proposing an enhancement or a PR with one.D-confusingDiagnostics: Confusing error or lint that should be reworked.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.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