Reputation: 4036
I ran into a confusing situation where what the compiler outputs doesn't logically make sense. Here is the minimal example to reproduce the same issue I'm having with my project code.
use std::sync::Arc;
struct A<'a, T> {
f: Box<dyn Fn(&u32) -> T + 'a>
}
struct B<'a> {
inner: A<'a, Z<'a>>
}
impl<'a, T> A<'a, T> {
fn new<F>(f: F) -> Self where F: Fn(&u32) -> T + 'a {
A { f: Box::new(f) }
}
}
struct X<'a> {
_d: &'a std::marker::PhantomData<()>
}
struct Z<'a> {
_d: &'a std::marker::PhantomData<()>
}
impl<'a> X<'a> {
fn g(&self, y: u32) -> Z {
Z { _d: &std::marker::PhantomData }
}
}
impl<'a> B<'a> {
fn new(x: Arc<X<'a>>) -> Self {
B {
inner: A::new(move |y: &u32| -> Z {
x.g(*y)
})
}
}
}
fn main() {
}
And the confusing compilation error:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> t.rs:35:19
|
35 | x.g(*y)
| ^
|
note: first, the lifetime cannot outlive the lifetime '_ as defined on the body at 34:27...
--> t.rs:34:27
|
34 | inner: A::new(move |y: &u32| -> Z {
| ^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `x`
--> t.rs:35:17
|
35 | x.g(*y)
| ^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 31:6...
--> t.rs:31:6
|
31 | impl<'a> B<'a> {
| ^^
= note: ...so that the expression is assignable:
expected B<'a>
found B<'_>
error: aborting due to previous error
What I don't quite get is what "the lifetime" refers to as mentioned in the log, and what exactly that anonymous lifetime '_
stands for.
Upvotes: 1
Views: 383
Reputation: 13942
There was a small oversight in your code. It should be:
impl<'a> X<'a> {
fn g(&self, y: u32) -> Z<'a> {
Z { _d: &std::marker::PhantomData }
}
}
The whole thing then compiles. This is an example of Rust's lifetime elision rule coming into play.
According to the relevant elision rules, namely:
- Each elided lifetime in input position becomes a distinct lifetime parameter.
- If there are multiple input lifetime positions, but one of them is
&self
or&mut self
, the lifetime ofself
is assigned to all elided output lifetimes.
Then to rustc
your original code will actually be like following:
impl<'a> X<'a> {
fn g<'b>(&'b self, y: u32) -> Z<'b> {
Z { _d: &std::marker::PhantomData }
}
}
The elided lifetime parameter 'b
will come from the calling site, which is exactly what you saw in the error message. The rustc
couldn't reconcile the two lifetimes, hence the error.
Upvotes: 2