Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot borrow `*self` as mutable more than once at a time when returning a Result containing a reference

Why is the following invalid and what should I do instead to make it work?

struct Foo;

impl Foo {
    fn mutable1(&mut self) -> Result<(), &str> {
        Ok(())
    }

    fn mutable2(&mut self) -> Result<(), &str> {
        self.mutable1()?;
        self.mutable1()?;
        Ok(())
    }
}

This code yields:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/lib.rs:10:9
   |
8  |     fn mutable2(&mut self) -> Result<(), &str> {
   |                 - let's call the lifetime of this reference `'1`
9  |         self.mutable1()?;
   |         ----           - returning this value requires that `*self` is borrowed for `'1`
   |         |
   |         first mutable borrow occurs here
10 |         self.mutable1()?;
   |         ^^^^ second mutable borrow occurs here

There are many questions already with the same error but I cannot use them to address this one as it's the presence of the implicit return provided by ? that causes the problem, without ? the code compiles successfully, yet with warnings.

Playground

like image 304
cYrus Avatar asked Oct 09 '19 00:10

cYrus


1 Answers

This is the same problem discussed in Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?. Through lifetime elision, the lifetime of the &str is tied to the lifetime of &self. The compiler isn't aware that the borrow won't be used in the condition that an Ok is returned. It's overly conservative and disallows this code. This is a limitation of the current borrow checker implementation.

If you did need the lifetime of the Err variant to be tied to the lifetime of the Foo instance, there's not much to be done in safe Rust (unsafe Rust is another story). In your case, however, it seems unlikely that your &str is intended to be tied to the lifetime of self, so you can use explicit lifetimes to avoid the problem. For example, a &'static str is a common basic error type:

impl Foo {
    fn mutable1(&mut self) -> Result<(), &'static str> {
        Ok(())
    }

    fn mutable2(&mut self) -> Result<(), &'static str> {
        self.mutable1()?;
        self.mutable1()?;
        Ok(())
    }
}

as it's the presence of the implicit return provided by ?

Not really, as the same code with explicit returns has the same problem:

fn mutable2(&mut self) -> Result<(), &str> {
    if let Err(e) = self.mutable1() {
        return Err(e);
    }
    if let Err(e) = self.mutable1() {
        return Err(e);
    }
    Ok(())
}
error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/lib.rs:12:25
   |
8  |     fn mutable2(&mut self) -> Result<(), &str> {
   |                 - let's call the lifetime of this reference `'1`
9  |         if let Err(e) = self.mutable1() {
   |                         ---- first mutable borrow occurs here
10 |             return Err(e);
   |                    ------ returning this value requires that `*self` is borrowed for `'1`
11 |         }
12 |         if let Err(e) = self.mutable1() {
   |                         ^^^^ second mutable borrow occurs here

like image 117
Shepmaster Avatar answered Sep 30 '22 03:09

Shepmaster