My code got a lifetime issue and I struggled for days and still can't solve it, here's a minimum reproducible code:
pub fn run<F>(f: F) -> ! where F: 'static + FnMut(), {
loop {
f();
}
}
struct Bar {}
struct Foo<'a> {
bar: Option<&'a Bar>,
}
impl<'a> Foo<'a> {
pub fn new_foo() -> Self {
Foo { bar: None }
}
pub fn add_bar(&mut self, bar: &'a Bar) {
self.bar = Some(bar);
}
}
fn main() {
let mut foo = Foo::new_foo();
let bar = Bar {};
run(move || {
foo.add_bar(&bar);
})
}
The idea is that new_foo() is very expensive in my case, so I want to create it first, instead of create in the closure, since run() will run the closure again and again.
But the compiler failed to compile:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:23:21
|
23 | foo.add_bar(&bar);
| ^^^^
|
note: first, the lifetime cannot outlive the lifetime `'_` as defined here...
--> src/main.rs:22:9
|
22 | run(move || {
| ^^^^^^^
note: ...so that closure can access `bar`
--> src/main.rs:23:21
|
23 | foo.add_bar(&bar);
| ^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src/main.rs:22:9: 24:6]` will meet its required lifetime bounds...
--> src/main.rs:22:5
|
22 | run(move || {
| ^^^
note: ...that is required by this bound
--> src/main.rs:1:35
|
1 | pub fn run<F>(f: F) -> ! where F: 'static + FnMut(), {
| ^^^^^^^
I'm always afraid of lifetime errors, but lucky thing is that compiler will give me suggestions in most time, but not for this one.
The run() function is a public API which cannot be changed, so how can I write the right code, so my design can work as I expected?
foo and bar are both moved into the closure. foo.add_bar(&bar) would need to turn the closure into a self-referential struct (since it places a reference to bar in to foo) which can’t work.
Interestingly, (presumably due to NLL) the error message is a lot shorter on nightly. Edit: Ah, the error message on nightly is about the same you get for this code on stable if you try compiling without that F: 'static bound.
The shorter message seems clearer, but I still can't found a solution, is there any way to only change codes in main() (or may be add_bar()) to accomplish my goal?
And whenever I meet some problems with closure, I always turn it to a struct for debugging. In this case, does
but calling that function requires exclusively borrowing TheClosure for the rest of its lifetime; after that borrow, it's only usable through the &mut, which is no longer available after the call and thus you can't actually call run_once again. This is, in fact, a way to create a self-referential (with &s) struct in safe Rust. It just has extremely niche utility. If you add back run for example, the multiple calls implied by the loop will give you an error. Your version with the loop inside run compiled only because it is a variation on run_once here.
And that's all moot in this case because calling the actual closure doesn't take a &'a mut self for some outside lifetime 'a anyway.
Self-referentiality aside, if you capture a Foo<'a>, you're not going to satisfy a 'static bound.
That's also a confusion of mine, I don't understand why the interface add a 'static bound. The real API is the winit crate, and it said:
/// Hijacks the calling thread and initializes the winit event loop with the provided
/// closure. Since the closure is `'static`, it must be a `move` closure if it needs to
/// access any data from the calling context.
///
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
/// event loop's behavior.
///
/// Any values not passed to this function will *not* be dropped.
///
/// [`ControlFlow`]: crate::event_loop::ControlFlow
#[inline]
pub fn run<F>(self, event_handler: F) -> !
where
F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
{
self.event_loop.run(event_handler)
}