NLL and RefCell


#1

Hi,

I have some code like the following that used to compile fine until yesterday (nightly-2018-01-03), but now fails with the latest nightly. Is this a regression with how NLL interacts with RefCell, or was the code never meant to be valid?

#![feature(nll)]
use std::cell::RefCell;

struct Foo {
    pub v: Vec<u32>,
    pub b: u32,
}

fn f(foo: RefCell<Foo>) {
    let mut foo = foo.borrow_mut();
    
    foo.v.push(foo.b);
    
}

fn main() {
    let foo: RefCell<Foo> = RefCell::new(Foo {v: Vec::new(), b: 0});
    f(foo);
}

The error I get is:

Compiling playground v0.0.1 (file:///playground)
error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
  --> src/main.rs:12:16
   |
12 |     foo.v.push(foo.b);
   |     ---        ^^^ immutable borrow occurs here
   |     |
   |     mutable borrow occurs here

error: aborting due to previous error

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

#2

no clue as to what’s happening (sorry!), but it will be easier to help you if you include the error(s) the compiler gives (when it comes to lifetime issues, even the “pros” need help :slight_smile: )


#3

Good point :slight_smile:️ Added the error to the original post.


#4

i find your structure a bit odd. Why not have the function inside the struct (under impl)? since you are anyway accessing the struct, i think it is better to have the function inside.


#5

That was just for the example. In the actual code it’s a method and the problem is the same. I’m more interested into why it suddenly stopped compiling.


#6

My hunch is this should work with NLL. @pnkfelix?

I realize this is illustration code but typically it’s easier to just get the &mut T out of the RefMut<T and then work with that reference:

let foo = &mut *foo.borrow_mut();

#7

Hmm, I didn’t know that trick! That should help, thanks.


#8

This sounds like a long existing issue with borrows in RefCell and the likes. Not a 100% sure, but I’ve experienced something similar here. As a correction this works:

#![feature(nll)]
use std::cell::RefCell;

struct Foo {
    pub v: Vec<u32>,
    pub b: u32,
}

fn f(foo: RefCell<Foo>) {
    let mut foo = foo.borrow_mut();
    let b = foo.b;
    foo.v.push(b);
    
}

fn main() {
    let foo: RefCell<Foo> = RefCell::new(Foo {v: Vec::new(), b: 0});
    f(foo);
}

PS: I think essentially the compiler first borrows mutably foo for the push and hence does not allow to reference any of its internals until the borrow is done. Similar to the issue I’ve pointed out these are compiler issues and depend on how the AST is parsed and borrows evaluated.


#9

If this was working on nightly up until a few days ago, then I suggest filing a github issue (please put the link in this thread if you do).


#10

Done: https://github.com/rust-lang/rust/issues/47224