This is very inconvenient to have that code not compiling: playground
It makes a lot of problems during embedded development, requiring to do stuff like Opaque, that makes code very unpleasant to read and write.
#![allow(dead_code)]
#![allow(clippy::all)]
#![allow(unused_must_use)]
fn main() {}
struct Opaque(&'static mut ());
fn use_static(_: &'static mut ()) { }
fn use_static2(op: Opaque) { use_static(op.0) }
fn compiles(foo: &'static mut (), bar: &'static mut ()) {
let foo = Opaque(foo);
let bar = Opaque(bar);
|| use_static2(foo) ;
move || use_static2(bar);
}
// #[cfg(skip)] // remove that line and it will not compile
fn error(foo: &'static mut (), bar: &'static mut ()) {
|| use_static(foo);
move || use_static(bar);
}
Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
--> src/main.rs:20:8
|
20 | || use_static(foo);
| -- ^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'static`
| |
| lifetime `'1` represents this closure's body
|
= note: closure implements `FnMut`, so references to captured variables can't escape the closure
error: lifetime may not live long enough
--> src/main.rs:21:13
|
21 | move || use_static(bar);
| ------- ^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'static`
| |
| lifetime `'1` represents this closure's body
|
= note: closure implements `FnMut`, so references to captured variables can't escape the closure
error: could not compile `playground` (bin "playground") due to 2 previous errors
I'm guessing the issue is that if the reference is only used in ways that could use reborrowing, the closure won't capture it by value. Regardless, this compiles:
fn compiles(foo: &'static mut (), bar: &'static mut ()) {
|| {
let foo = foo;
use_static(foo);
};
move || {
let bar = bar;
use_static(bar);
};
}
A closure which does not move out of any captured variables implements FnMut, indicating that it can be called by mutable reference.
Note: move closures may still implement Fn or FnMut, even though they capture variables by move. This is because the traits implemented by a closure type are determined by what the closure does with captured values, not how it captures them.
As was evident (though not obvious that it was critical) in the error message, the error is due to the closure "looking like" it should implement FnMut, when that implementation is not actually possible due to lifetimes.
Yes, we need some better way to override closure inferences generally.
Another workaround for the playground is to funnel the closure through an identity function expecting FnOnce, as that will influence the compiler to not implement FnMut.
fn funnel<F: FnOnce()>(f: F) -> F { f }
fn error(foo: &'static mut (), bar: &'static mut ()) {
funnel(|| use_static(foo));
funnel(move || use_static(bar));
}