Christoph
Christoph

Reputation: 27985

Rust ownership rules in the context of closures

I'm playing with the new closures in Rust and I ran into a situation that again showed me I didn't fully grasp the ownership rules yet.

#![feature(unboxed_closures)]
#![feature(overloaded_calls)]

fn main() {
    let mut test = 5i;
    let do_something = ref |:| { println!("{}", test) };
    test = 6i;
    do_something();

    println!("{}", test);
}

This code fails telling me that the assignment test = 6i; is invalid because test is already borrowed to the do_something closure.

But reading the rules of ownership (from the Rust Guide) it seems valid to me:

1.You control when that resource is deallocated.

2.You may lend that resource, immutably, to as many borrowers as you'd like.

3.You may lend that resource, mutably, to a single borrower. BUT

4.Once you've done so, you may not also lend it out otherwise, mutably or immutably.

5.You may not lend it out mutably if you're currently lending it to someone.

What is the problem with the code above. test is the owner and do_something should only have an immutable reference. Shouldn't it be legal to change the value of test if the only reference to it that we lend out to someone was immutable?

FOLLOW UP

Now that I learned that access by the owner also counts as lending I changed the code to use Cell.

#![feature(unboxed_closures)]
#![feature(overloaded_calls)]

use std::cell::Cell;

fn main() {
    let test = Cell::new(5i);
    let do_something = ref |:| { println!("{}", test) };
    test.set(6i);
    do_something();

    println!("{}", test);
}

But I wonder why this prints

5
6

and not

6
6

How would I need to change the code to print this?

6
6

Upvotes: 1

Views: 417

Answers (2)

Vladimir Matveev
Vladimir Matveev

Reputation: 127751

Answer to your follow-up:

This seems to happen because Cell is Copy, so it is copied into the closure. If you do this (now ref before the closure does not matter and can be omitted):

#![feature(unboxed_closures)]
#![feature(overloaded_calls)]

use std::cell::Cell;

fn main() {
    let test = Cell::new(5i);
    let test_ref = &test;
    let do_something = |:| { println!("{}", test_ref.get()) };
    test.set(6i);
    do_something();

    println!("{}", test.get());
}

Then it prints two 6s.

However, I think this behavior is somewhat unexpected. I certainly would expect that closures with ref prefix will take all their environment by reference. I've just submitted an issue for this.

Upvotes: 2

Levans
Levans

Reputation: 14992

As you quoted yourself :

You may not lend it out mutably if you're currently lending it to someone.

As you're already lending test to do_something, you cannot access it mutably before do_something goes out of scope.

Upvotes: 2

Related Questions