Reputation: 2293
I have some types like this, where they each have a lifetime parameter.
use std::marker::PhantomData;
use std::ops::Index;
pub struct Foo<'ctx> {
bars: Vec<Bar<'ctx>>,
phantom: PhantomData<&'ctx ()>,
}
impl Foo<'_> {
pub fn get_bar(&self, index: usize) -> Option<&Bar> {
self.bars.get(index)
}
}
pub struct Bar<'ctx> {
// pretend we are using a context here
phantom: PhantomData<&'ctx ()>,
}
I'd like to implement Index
for Foo
, but the compiler doesn't like it:
impl <'ctx> Index<usize> for Foo<'ctx> {
type Output = Bar<'ctx>;
fn index(&self, _index: usize) -> &Self::Output {
self.get_bar(_index).unwrap()
}
}
I get this error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/ir/foo.rs:24:14
|
24 | self.get_bar(_index).unwrap()
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
--> src/ir/foo.rs:23:14
|
23 | fn index(&self, _index: usize) -> &Self::Output {
| ^^^^^
note: ...so that reference does not outlive borrowed content
--> src/ir/foo.rs:24:9
|
24 | self.get_bar(_index).unwrap()
| ^^^^
note: but, the lifetime must be valid for the lifetime `'ctx` as defined here...
--> src/ir/foo.rs:20:7
|
20 | impl <'ctx> Index<usize> for Foo<'ctx> {
| ^^^^
note: ...so that the types are compatible
--> src/ir/foo.rs:23:53
|
23 | fn index(&self, _index: usize) -> &Self::Output {
| _____________________________________________________^
24 | | self.get_bar(_index).unwrap()
25 | | }
| |_____^
= note: expected `<Foo<'ctx> as Index<usize>>`
found `<Foo<'_> as Index<usize>>`
(I see a few similar questions on SO but they all seem to differ in the particulars, namely that the Output type has a lifetime parameter).
Upvotes: 1
Views: 246
Reputation: 4775
This is due to that the rust compiler cannot infer proper lifetimes when there are multiple elided lifetimes. The rust compiler can only take the omitted lifetimes as a same lifetime '_
.
The inferred signature of the function get_bar
is actually:
pub fn get_bar<'_>(self: &'_ Foo<'_>, index: usize) -> Option<&'_ Bar<'_>>
Note that all '_
refer to the same lifetime, this is obviously not what we need, because we don't have to keep borrowing the value of some Bar
during the whole lifetime of 'ctx
.
And, the inferred signature of the function index
is:
fn index<'_>(self: &'_ Foo<'ctx>, _index: usize) -> &'_ Bar<'ctx>
Which is more generic than the signature of get_bar
because it allows the lifetime argument of '_
to be shorter than 'ctx
, therefore you cannot call get_bar
within the body of index
To make it work, you have to specify an explicit 'ctx
, because the elided lifetime '_
has to be the lifetime of the borrowed self
.
impl<'ctx> Foo<'ctx> {
pub fn get_bar(&self, index: usize) -> Option<&Bar<'ctx>> {
self.bars.get(index)
}
}
And the signature of get_bar
now turns to be:
pub fn get_bar<'_>(self: &'_ Foo<'ctx>, index: usize) -> Option<&'_ Bar<'ctx>>
Then the lifetimes in index
could match the lifetimes in get_bar
Upvotes: 4