In this "captured variable cannot escape `FnMut`" error, what is the "returned reference"?

Viewed 223

What is going wrong here?

fn main() {
    // scope 1
    let mut i = 0u32;
    let _ = (0..2)
        .map(|_| {
            // scope 2
            // error : captured variable cannot escape `FnMut` closure body
            (0..2).map(|_| {
                // scope 3
                i += 1;
            })
        });
}

playground

The full error is:

error: captured variable cannot escape `FnMut` closure body
  --> src/main.rs:8:13
   |
3  |       let mut i = 0u32;
   |           ----- variable defined here
4  |       let _ = (0..2)
5  |           .map(|_| {
   |                  - inferred to be a `FnMut` closure
...
8  | /             (0..2).map(|_| {
9  | |                 // scope 3
10 | |                 i += 1;
   | |                 - variable captured here
11 | |             })
   | |______________^ returns a reference to a captured variable which escapes the closure body
   |
   = note: `FnMut` closures only have access to their captured variables while they are executing...
   = note: ...therefore, they cannot allow references to captured variables to escape

What is going on?

  1. Why does adding move to parameter list of the second closure make the problem go away?
  2. The full error message says that the second closure "returns a reference to a captured variable which escapes the closure body"? But the function returns unit. So what is rustc thinking? And what are the references? Is the i in scope 3 really a reference to an invisible reference in scope 2 to the i in scope 1?
  3. Why does changing the second map to for_each make the problem go away?
1 Answers

Adding a semicolon at the end of the inner map() makes the problem go away: the inner map() containing mutable reference to i is not returned to the outer map(), but you need to iterate through these map()s in order to alter i.

Using move in the inner map() gets rid of the reference to i by copying i into the closure so that the increment has no effect on the original i.

I don't really understand your original intent, but this seems to work:

fn main() {
    let mut i = 0u32;
    let outer = (0..2).map(|_| {
        let inner = (0..2).map(|_| {
            i += 1;
        });
        for inner_elem in inner {
            println!("inner_elem={:?}", inner_elem);
        }
    });
    for outer_elem in outer {
        println!("outer_elem={:?}", outer_elem);
    }
    println!("i={:?}", i);
}
/*
inner_elem=()
inner_elem=()
outer_elem=()
inner_elem=()
inner_elem=()
outer_elem=()
i=4
*/
Related