Skip to content

Closures-Capturing 9.2.1 sample code needs update for rust 2018 #1169

Closed
@1414C

Description

@1414C

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).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions