dlwh
dlwh

Reputation: 2293

Implementing Index for type with a lifetime parameter

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

Answers (1)

Alsein
Alsein

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

Related Questions