Skip to content
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

NLL issue when using DerefMut to access fields through a Pin, causing multiple mutable borrow error #63993

Open
dialtone opened this issue Aug 29, 2019 · 2 comments
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one. NLL-complete Working towards the "valid code works" goal

Comments

@dialtone
Copy link

dialtone commented Aug 29, 2019

The code below doesn't compile due to multiple mutable borrows, however the exact same code without the std::pin::Pin compiles fine actually. My expectation is however that the code would obviously compile fine in both cases.

use std::pin::Pin;
use slab;

struct Entry {
    complete: u64
}

struct Ctx {}
impl Ctx {
    fn new() -> Self {
        Ctx{}
    }
    fn pread<T>(&mut self, _key: T) -> std::io::Result<()> {
        Ok(())
    }
}

struct Foo {
    ctx: Ctx,
    handles: slab::Slab<Entry>
}
impl Foo {
    fn new() -> Self {
        Foo {ctx: Ctx::new(),
             handles: slab::Slab::with_capacity(20)}
    }
    
    fn blah(mut self: Pin<&mut Self>) {
        let v = self.handles.vacant_entry();
        let key = v.key();
        match self.ctx.pread(key) {
            Ok(()) => {
                v.insert(Entry{complete:123});
            },
            Err(_) => {
                
            }
        }
        
    }
}

fn main() {
    Pin::new(&mut Foo::new()).blah();
}

The exception raised is:

error[E0499]: cannot borrow `self` as mutable more than once at a time
  --> src/main.rs:31:15
   |
29 |         let v = self.handles.vacant_entry();
   |                 ---- first mutable borrow occurs here
30 |         let key = v.key();
31 |         match self.ctx.pread(key) {
   |               ^^^^ second mutable borrow occurs here
32 |             Ok(()) => {
33 |                 v.insert(Entry{complete:123});
   |                 - first borrow later used here

error: aborting due to previous error

I'm able to get the code to compile also by creating additional scopes around the areas where I'm supposedly borrowing self like this:

let key = {
    let entry = self.handles.vacant_entry();
    entry.key()
};
match self.ctx.pread(key) {
    Ok(()) => {
        *self.handles.get_mut(key).unwrap() =
            Entry { complete: 123 };
    }

Although obviously that's pretty awkward. I'm told that this could possibly be a bug in the NLL, but I'm pretty new to the language so I'm just reporting this with a short reproducible example.

Hope it helps.

@matthewjasper matthewjasper added C-enhancement Category: An issue proposing an enhancement or a PR with one. NLL-complete Working towards the "valid code works" goal labels Aug 29, 2019
@tmiasko
Copy link
Contributor

tmiasko commented Aug 30, 2019

The self is a Pin<&mut Self>. Obtaining mutable references to the fields
will require going through DerefMut, which requires unique borrow. This isn't
a NLL bug.

You can avoid the issue by dereferencing self once on entry to the method
let this = &mut *self, and then continue accessing it through this.

@dialtone
Copy link
Author

Alright, thanks for the answer, it's a bit awkward anyway but it makes sense. Given however that in the discord channel other people were also confused by this, and now that I know what to search for, I see many questions asked online about this very same case for other types that implement DerefMut (like RefCell, Box and so on), maybe the error message returned by the language could explain a bit better that it could happen also because of this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one. NLL-complete Working towards the "valid code works" goal
Projects
None yet
Development

No branches or pull requests

3 participants