Consider the signature of the function calls_closure below. To me it looks like that there is no way that closure: impl FnOnce passed to it can make its way to its return value, as both are declared to have independent lifetimes. If anything related to closure was returned by calls_closure, I would expect a 'closure: 'result bound. But the function's signature does not have any.
Still, the compiler complains that when calling calls_closure from creates_closure the lifetime 'closure_state which is captured by the closure could make its way through calls_closure and then be returned by creates_closure.
To me, this looks like a contradiction. Can anyone explain what is going on here? Is there anything "magic" about closure and lifetimes I am not aware of? Is there a way to add lifetime constraints only to calls_closure to make this exact code compile (the idea is that creates_closure's return value is lifetime-independent of closure_state)?
// the compiler thinks that the closure could be returned by by this function
// to me, the function signature indicates that the closure and the return value are independent
fn calls_closure<'closure, 'result>(
closure: impl 'closure + FnOnce() -> (),
) -> impl 'result + Iterator<Item = i32> {
closure();
[2].into_iter()
}
fn creates_closure<'closure_state, 'result>(
closure_state: &'closure_state mut i32,
) -> impl 'result + Iterator<Item = i32> {
calls_closure(|| {
*closure_state += 1;
})
}
Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
--> src/lib.rs:13:5
|
10 | fn creates_closure<'closure_state, 'result>(
| -------------- ------- lifetime `'result` defined here
| |
| lifetime `'closure_state` defined here
...
13 | / calls_closure(|| {
14 | | *closure_state += 1;
15 | | })
| |______^ function was supposed to return data with lifetime `'result` but it is returning data with lifetime `'closure_state`
|
= help: consider adding the following bound: `'closure_state: 'result`
error: could not compile `playground` due to previous error
The compiler proposes to change the second function creates_closure, but that is the one I do not want to change. I want to communicate to the compiler that in the first function calls_closure, the closure gets "consumed" in a way that nothing that is borrowed by the closure gets returned by calls_closure.
This feels like a bug in the borrow checker/lifetime system.
I would expect the following snippet to compile because the closure passed to calls_closure() is completely unrelated to the impl Iterator<...> we return. I've even added a + 'static to indicate that the returned iterator only borrows from a 'static variable ([2] should be promoted to a const by the compiler via "rvalue static promotion").