Help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`

Hi,

The following code is not compiling for me, and I struggle to understand why:

pub trait ValuedArray<T> {
    fn value(&self, i: usize) -> T;
}

struct Binary {
    a: Vec<u8>
}

impl ValuedArray<&[u8]> for Binary {
    fn value(&self, i: usize) -> &[u8] {
        &self.a[i..i+1]
    }
}

struct Int {
    a: Vec<i32>
}

impl ValuedArray<i32> for Int {
    fn value(&self, i: usize) -> i32 {
        self.a[i]
    }
}

fn main() {}

Rust-playground

with the error

  = note: expected `fn(&Binary, _) -> &[u8]`
             found `fn(&Binary, _) -> &[u8]`
help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`

The error message is telling me something like A != A, which is not really helpful, but is also hinting to a lifetime mismatch. Any ideas of why this is happening and what I need to do to address it?

Note that the implementation above is a simplification of more complex code, but the gist is: I am building an interface with .value that I can use in generics to restrict interfaces to implement .value(i).

Not sure I can explain this really well but here's one way of looking at it.

pub trait ValuedArray<T> {
    fn value(&self, i: usize) -> T;

"If a type implements ValuedArray<T>, you can borrow it (with any lifetime) to get a T."

impl ValuedArray<i32> for Int {
    fn value(&self, i: usize) -> i32 {

"Int implements ValuedArray<i32>, so you can borrow an Int (with any lifetime) to get an i32."

impl ValuedArray<&[u8]> for Binary {
// is equivalent to:
impl<'a> ValuedArray<&'a [u8]> for Binary {

"Binary implements ValuedArray<&'a [u8]>, for any lifetime 'a, so you should be able to borrow a Binary (with any lifetime) to get a &'a [u8]; but..."

    fn value(&self, i: usize) -> &[u8] {
    // is equivalent to:
    fn value<'b>(&'b self, i: usize) -> &'b [u8] {

"... in fact you can only borrow a Binary to get a &'b [u8] of one particular lifetime 'b (i.e., the lifetime of the borrow of self)."

If you annotate all the lifetimes explicitly, you do get a slightly better help message:

   = note: expected `fn(&Binary, _) -> &'a [u8]`
              found `fn(&Binary, _) -> &[u8]`

You can look up the lifetime elision rules to understand how the compiler decides what lifetimes things have when you don't specify any.

To solve your problem, you need to tell the compiler how the input lifetime is really related to the output lifetime. You can do that in the trait itself by limiting value to only returning references:

pub trait ValuedArray<T: ?Sized> {  // `?Sized` is necessary to allow `T` to be `[u8]`
    fn value(&self, i: usize) -> &T;  // bake in the reference assumption here
}

struct Binary {
    a: Vec<u8>
}

impl ValuedArray<[u8]> for Binary {
    fn value(&self, i: usize) -> &[u8] {
        &self.a[i..i+1]
    }
}

Or you can do it in the impl, by implementing the trait for references so the lifetime is nameable when you implement it:

pub trait ValuedArray<T> {
    fn value(self, i: usize) -> T;
}

struct Binary {
    a: Vec<u8>
}

impl<'a> ValuedArray<&'a [u8]> for &'a Binary {
    fn value(self, i: usize) -> &'a [u8] {
        &self.a[i..i+1]
    }
}

Either way you do it will also have implications for how you implement ValuedArray for Int. Without knowing anything special about your use case, I'd prefer the first thing.

3 Likes

A third option is to lift the lifetime to be a trait parameter, which allows the original method signature to stand:

pub trait ValuedArray<'a, T:'a> {
    fn value(&'a self, i: usize) -> T;
}

impl<'a> ValuedArray<'a, &'a [u8]> for Binary { /* ... */ }
impl<'a> ValuedArray<'a, i32> for Int { /* ... */ }

(Playground)

4 Likes