Description
While working through Chapter 9
-> 9.2 Closures
-> 9.2.1 Capturing
I found that execution of the following code snippet resulted in an error (as expected) when executed in the embedded playground:
let mut count = 0;
// A closure to increment `count` could take either `&mut count`
// or `count` but `&mut count` is less restrictive so it takes
// that. Immediately borrows `count`.
//
// A `mut` is required on `inc` because a `&mut` is stored inside.
// Thus, calling the closure mutates the closure which requires
// a `mut`.
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
// Call the closure.
inc();
inc();
// expect a borrow failure here
let _reborrow = &mut count;
// ^ TODO: try uncommenting this line.
As expected, uncommenting the let _reborrow ...
line resulted in an error:
Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `count` as mutable more than once at a time
--> src/main.rs:35:26
|
26 | let mut inc = || {
| -- first mutable borrow occurs here
27 | count += 1;
| ----- previous borrow occurs due to use of `count` in closure
...
35 | let _reborrow = &mut count;
| ^^^^^ second mutable borrow occurs here
...
54 | }
| - first borrow ends here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0499`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.
Executing the same code on my workstation did not result in an error and a cargo check
of the code returned a clean result:
cargo check
Checking closures v0.1.0 (/<my_path>/rust_by_example/closures)
Finished dev [unoptimized + debuginfo] target(s) in 0.23s
I am running the following build: stable-x86_64-apple-darwin unchanged - rustc 1.33.0 (2aa4c46cf 2019-02-28). / rust 2018
Presumably some changes have been made to rustc
v1.33 / 2018 whereby it recognizes that closure variable inc
is not referenced again in the code following the two inc();
calls? I am pretty new to Rust, by I think that the compiler is smart enough to recognize that the new assignment to _reborrowed
is safe in this situation?
I updated the code snippet on my workstation as shown below, and found that the complier generated an error (as expected):
let mut count = 0; // i32
// a closure to increment `count` could take `&mut count` or
// `count` but `&mut count` is less restrictive, so it takes
// that(?). `count` is immediately borrowed.
// `mut` is required on `inc` because a `&mut` is stored inside.
// callint the closure mutates the closure which in-turn requires
// a `mut`.
let mut inc = || {
// count: &mut i32
count += 1;
println!("`count`: {}", count);
};
// Call the closure.
inc();
let _reborrow = &mut count; // new location!
inc();
Error message:
error[E0499]: cannot borrow `count` as mutable more than once at a time
--> src/main.rs:48:21
|
40 | let mut inc = || {
| -- first mutable borrow occurs here
41 | // count: &mut i32
42 | count += 1;
| ----- first borrow occurs due to use of `count` in closure
...
48 | let _reborrow = &mut count;
| ^^^^^^^^^^ second mutable borrow occurs here
49 | inc();
| --- first borrow later used here
error: aborting due to previous error
I am pretty sure that this is the scenario that the page author wanted to illustrate, as this is the behaviour exhibited by the embedded playground (and also with edition = "2015"). The concern is that the example as written illustrates the author's intent in the playground, but not in the current stable release (1.33)/2018 of the rust toolchain.
The code example should probably(?) be enhanced to illustrate / compensate for the effects described above. Maybe something like this:
let mut count = 0;
// A closure to increment `count` could take either `&mut count`
// or `count` but `&mut count` is less restrictive so it takes
// that. Immediately borrows `count`.
//
// A `mut` is required on `inc` because a `&mut` is stored inside.
// Thus, calling the closure mutates the closure which requires
// a `mut`.
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
// Call the closure.
inc();
// let _reborrow = &mut count; // error[E0499]: ...
// ^ TODO: try uncommenting this line.
inc();
// let _reborrow = &mut count; // okay!
// ^ TODO: try uncommenting this line.
Thanks for looking (and also for putting together such a great book).