Skip to content

Two MUTABLE borrows to local var, by pushing closures into RefCell'd Vec through a trait. #18783

Closed
@cristicbz

Description

@cristicbz

Looks like a bug in the borrow checker:

use std::cell::RefCell;

fn main() {
    let c = Caller::new();
    let mut y = 1;  // NOT in RefCell.
    c.pusher().push(|| { y = y * 2u; });
    c.pusher().push(|| { y = y * 3u; });  // Second mutable borrow of y?!
    c.call();
    println!("{}", y); // Prints 6.
}

// Collects multiple closures then call all of them.
struct Caller<'c> {
    funs: RefCell<Vec<||:'c -> ()>>,
} impl<'c> Caller<'c> {
    fn new() -> Caller<'c> {
        Caller { funs: RefCell::new(Vec::new()) }
    }

    fn pusher(&'c self) -> Pusher<'c> {
        Pusher { caller: self }
    }

    fn call(&self) {
        let ref mut funs = self.funs.borrow_mut();
        loop {
            match funs.pop() {
                Some(f) => f(),
                _ => break,
            }
        }
    }
}

// Pushes closures into a caller.
trait Push<'c> {
    fn push<'f: 'c>(self, push: ||:'f -> ());
}

struct Pusher<'c> {
    caller: &'c Caller<'c>
}
// /*
impl<'c> Push<'c> for Pusher<'c> {
    fn push<'f: 'c>(self, fun: ||:'f -> ()) {
        self.caller.funs.borrow_mut().push(fun)
    }
}
// Using the following impl (without trait) makes compilation fail correctly: */
/*
impl<'c> Pusher<'c> {
    fn push<'f: 'c>(self, fun: ||:'f -> ()) {
        self.caller.funs.borrow_mut().push(fun)
    }
}
*/

Playpen

This compiles and runs fine. It also compiles when using unboxed closures instead (using type param F: FnOnce<(), ()>+'c and F members). Using the non-trait impl, however, causes compilation to fail correctly:

<anon>:7:21: 7:39 error: cannot borrow `y` as mutable more than once at a time
<anon>:7     c.pusher().push(|| { y = y * 3u; });  // Second mutable borrow of y?!
                             ^~~~~~~~~~~~~~~~~~
<anon>:7:30: 7:31 note: borrow occurs due to use of `y` in closure
<anon>:7     c.pusher().push(|| { y = y * 3u; });  // Second mutable borrow of y?!
                                      ^
<anon>:6:21: 6:39 note: previous borrow of `y` occurs here due to use in closure; the mutable borrow prevents subsequent moves, borrows, or modification of `y` until the borrow ends
<anon>:6     c.pusher().push(|| { y = y * 2u; });
                             ^~~~~~~~~~~~~~~~~~
<anon>:10:2: 10:2 note: previous borrow ends here
<anon>:3 fn main() {
...
<anon>:10 }
          ^
<anon>:9:20: 9:21 error: cannot borrow `y` as immutable because it is also borrowed as mutable
<anon>:9     println!("{}", y); // Prints 6.
                            ^
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
<anon>:9:5: 9:23 note: expansion site
<anon>:6:21: 6:39 note: previous borrow of `y` occurs here due to use in closure; the mutable borrow prevents subsequent moves, borrows, or modification of `y` until the borrow ends
<anon>:6     c.pusher().push(|| { y = y * 2u; });
                             ^~~~~~~~~~~~~~~~~~
<anon>:10:2: 10:2 note: previous borrow ends here
<anon>:3 fn main() {
...
<anon>:10 }
          ^
<anon>:9:20: 9:21 error: cannot borrow `y` as immutable because it is also borrowed as mutable
<anon>:9     println!("{}", y); // Prints 6.
                            ^
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
<anon>:9:5: 9:23 note: expansion site
<anon>:7:21: 7:39 note: previous borrow of `y` occurs here due to use in closure; the mutable borrow prevents subsequent moves, borrows, or modification of `y` until the borrow ends
<anon>:7     c.pusher().push(|| { y = y * 3u; });  // Second mutable borrow of y?!
                             ^~~~~~~~~~~~~~~~~~
<anon>:10:2: 10:2 note: previous borrow ends here
<anon>:3 fn main() {
...
<anon>:10 }
          ^
error: aborting due to 3 previous errors
playpen: application terminated with error code 101
Program ended.

Metadata

Metadata

Assignees

Labels

E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions