Closures should infer to capture by copy

eed59be
Opened by bluss at 2022-03-22 08:50:26

Inspired by a common stumbling block with Iterator::flat_map.

This code does not compile:

(0..10).flat_map(|i| (0..i).map(|j| i + j))
error: `i` does not live long enough

(playground code)

The problem: i is a local variable and captured by reference in a closure we're trying to return. It's an integer, its data size is equal or smaller as a value instead of a reference.

Can rustc infer that the closure should capture i by "move" here (Copy)?

  1. That seems a bit magical. What if i were mutable?

    Steven Allen at 2016-09-18 21:01:02

  2. The compiler can already infer move sometimes, for example when you consume a value like this. This compiles without error:

    (0..10).flat_map(|_| { let s = String::new(); Some(1).map(|_| s) });
    

    (It's valid to return because the closure to Option::map is an FnOnce, but FnOnceness is really orthogonal to capture mode.)

    I don't immediately see the issue with mutability. It works the same to the closure if a variable is captured by mutable reference or copy, you can mutate it anyway.

    (0..10).flat_map(|mut i| (0..10).map(move |j| { i += j; i }));  // this is fine
    

    bluss at 2016-09-18 21:16:27

  3. I don't immediately see the issue with mutability. It works the same to the closure if a variable is captured by mutable reference or copy, you can mutate it anyway.

    I was more worried about the case where something outside the closure mutates i. However, that would require a Copy type with interior mutability (which I don't think is even possible).

    The compiler can already infer move sometimes, for example when you consume a value like this.

    Given this, I'd have to agree. Note: "sometimes" is when the closure is FnOnce.

    Steven Allen at 2016-09-19 01:45:13

  4. That would be lifetime inference affecting type inference. I don't feel like this is going to happen.

    Ariel Ben-Yehuda at 2016-09-24 15:43:42

  5. @arielb1 That I can absolutely understand. But do you think it would have to be like that, could it not simply infer to Copy here because it can copy and preserve the exact same functionality in the closure?

    bluss at 2016-09-24 17:09:35

  6. @bluss

    But that won't help when i is not Copy.

    Without nested closures, we already have a "closure may outlive the current function" error message, but it is not shown there because of laziness. This issue was already known since we had unboxed closures.

    Ariel Ben-Yehuda at 2016-09-25 09:13:05

  7. Triage: no change

    Maayan Hanin at 2022-03-22 08:50:26