Reputation: 27985
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
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 6
s.
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
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