Skip to content

Vec iterator causes double mutable borrow compile error #43437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ivanbakel opened this issue Jul 23, 2017 · 2 comments
Closed

Vec iterator causes double mutable borrow compile error #43437

ivanbakel opened this issue Jul 23, 2017 · 2 comments
Labels
A-borrow-checker Area: The borrow checker

Comments

@ivanbakel
Copy link
Contributor

ivanbakel commented Jul 23, 2017

Alternative title: Multiple simultaneous overlapping mutable borrows.

I shouldn't find an existing issue on this, and it doesn't seem to me like a code error - at least, nothing in the documentation suggested to me that this was expected. At the very least, the error message is unclear if it is expected.

MRE:

struct Test<'a, T : 'a> {
    contents : &'a mut T,
    function : &'a fn(&'a mut T) -> ()
}

impl<'a, T : 'a> Test<'a, T> {
    fn do_thing(self) {
        let v : Vec<()> = vec![];
        for item in v.iter() {
            match (self.function)(self.contents) {
                () => {}
            }
        }
    }
}

Expectation:
This would compile. The mutable borrows don't overlap.

What happens:
rustc complains that self.contents is mutably borrowed twice at the same position.

 error[E0499]: cannot borrow `*self.contents` as mutable more than once at a time
   |
28 |             match (self.function)(self.contents) {
   |                                   ^^^^^^^^^^^^^
   |                                   |
   |                                   second mutable borrow occurs here
   |                                   first mutable borrow occurs here
...
32 |     }
   |     - first borrow ends here

This is the only compiler error in the program. Any fixes to this error will cause the entire thing to compile successfully.

From testing, things that will prevent this:

  • Removing the outside vec iterator
  • Using a named external function with the same signature instead of a function from a field of Test.

Things that will not prevent this:

  • Using a function impl'd on a field of Test - e.g.

     struct FunctionWrapper<'a, T : 'a> { inner_func : ... }
     impl<a, T : 'a> FunctionWrapper<'a, T> { fn call(&self, arg : &'a mut T) { (self.inner_func)(arg) } }
     struct Test<'a, T : 'a> { function : FunctionWrapper<...> }
    

    This is true even if you're not calling a stored function - a stored function was just the simplest example I could think of.

  • Passing in the value which is the argument of the function. fn do_thing(self, other : &'a mut T) will still complain of a double borrow at the same position of other

Meta

Tested on

  • rustc 1.19.0 (stable)
  • rustc 1.20.0-nightly
@GuillaumeGomez GuillaumeGomez added the A-borrow-checker Area: The borrow checker label Jul 24, 2017
@cuviper
Copy link
Member

cuviper commented Jul 24, 2017

The problem is in the declaration fn(&'a mut T) -- you're giving the function a reference for the full lifetime 'a, and since &mut is invariant this can't be reduced to borrow only during one loop iteration. It compiles if you change this to function: &'a fn(&mut T) -> (), which uses an anonymous lifetime akin to a HRTB like function: &'a for<'b> fn(&'b mut T) -> ().

@ivanbakel
Copy link
Contributor Author

Thanks for the answer! Good to know it's not actually a bug, and the explanation does make sense. I'll see about making a PR to adjust the error message slightly if the mut borrow is happening at the same place twice - that was probably the most confusing part of all this.

bors added a commit that referenced this issue Jul 27, 2017
Extended error message for mut borrow conflicts in loops

RFC issue: rust-lang/rfcs#2080

The error message for multiple mutable borrows on the same value over loop iterations now makes it clear that the conflict comes from the borrow outlasting the loop. The wording of the error is based on the special case of the moved-value error for a value moved in a loop. Following the example of that error, the code remains the same for the special case.

This is mainly because I felt the current message is confusing in the loop case : #43437. It's not clear that the two conflicting borrows are in different iterations of the loop, and instead it just looks like the compiler has an issue with a single line.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-borrow-checker Area: The borrow checker
Projects
None yet
Development

No branches or pull requests

3 participants