'Lifetime in impl does not match method in trait

Code

Playground (Stable Rust 1.45.0, 2018 edition) No external crates needed for example.

type Error = Box<dyn std::error::Error>;
type Result<R=()> = std::result::Result<R, Error>;

struct Arena;

pub trait AsSized<'a> {
    type AsSized: Sized + 'a;
}
impl<'a, T: Sized + 'a> AsSized<'a> for T {
    type AsSized = Self;
}
impl<'a, T: AsSized<'a>> AsSized<'a> for [T] {
    type AsSized = &'a [T::AsSized];
}

pub trait Format<T>: Send + Sync
where T: ?Sized
{
    fn get_bytes<'a>(&self, value: &'a T, arena: &'a Arena) -> Result<&'a [u8]>;
    fn get_value<'a>(&self, bytes: &'a [u8], arena: &'a Arena) -> Result<T::AsSized>
    where T: AsSized<'a>;
}

struct RawBytes;

impl Format<[u8]> for RawBytes
where [u8]: for<'a> AsSized<'a, AsSized=&'a [u8]>
{
    fn get_bytes<'a>(&self, value: &'a [u8], _arena: &'a Arena) -> Result<&'a [u8]> {
        Ok(value)
    }
    fn get_value<'a>(&self, bytes: &'a [u8], arena: &'a Arena) -> Result<<[u8] as AsSized<'a>>::AsSized> {
        Ok(bytes)
    }
}

Issue

I'm getting a compiler error on the impl of get_value for RawBytes:

error[E0195]: lifetime parameters or bounds on method `get_value` do not match the trait declaration

I don't understand what the problem is. It seems like the lifetime specification is the same between both definitions. How do I write the impl of get_value for RawBytes so that it works?

I would think that since u8: Sized then <u8 as AsSized<'a>>::AsSized = u8 and then <[u8] as AsSized<'a>>::AsSized = &'a [u8] but it seems like that isn't the case?

Background

Format takes an arena based allocator and converts a slice of bytes to and from a complex type. I plan to write an adapter for various Serde formats. RawBytes is a trivial implementaiton of Format for a slice of bytes that just returns the original slice.

Both methods of Format are allowed to return a value that borrows from the input value. The format itself may be shared between threads, so the lifetime of self is unrelated to the returned value.

The purpose of AsSized is to allow dynamically sized types like str and [u8] to be used directly, but since dynamically sized types can't be returned directly, AsSized provides a sized equivalent for any type; dynamically sized types return a reference to the DST instead (borrowed from the arena). Sized types like u8 that can be returned directly have an AsSized type of self

Also tried

fn get_value<'a>(&self, bytes: &'a [u8], arena: &'a Arena) -> Result<'a [u8]>

I tried simplifying the impl's get_value to just name the slice directly; rust then says that the impl for get_value is missing entirely.

fn get_value<'a>(&self, bytes: &'a [u8], arena: &'a Arena) -> Result<&'a [<u8 as AsSized<'a>>::AsSized]>

This give the same "lifetime parameters... do not match" error.



Solution 1:[1]

Partial answer (i got stuck too :)


I kept deleting code until I reached this minimal example that reproduces the error:

pub trait AsSized<'a> {
    type AsSized: Sized + 'a;
}

pub trait Format<T>
where
    T: ?Sized,
{
    fn get_value<'a>(&self, bytes: &'a [u8])
    where
        T: AsSized<'a>;
}

impl Format<[u8]> for () {
    fn get_value<'a>(&self, bytes: &'a [u8]) {
        todo!()
    }
}

It now becomes apparent that, in the trait definition, there is a further constraint on the 'a lifetime: we require that T: AsSized<'a>, and this is what's causing the error. You don't have such a clause on the impl block, hence the compiler doesn't accept the lifetimes as equivalent.

(I'm not sure if they're actually incompatible or if this is a limitation of the compiler)

So I decided to add where [u8]: AsSized<'a> to the impl function as well. To get it to compile, I also:

  • Made Arena public (type leak issue)
  • Removed where [u8]: for<'a> AsSized<'a, AsSized=&'a [u8]> from the impl (this where clause makes no sense to me – it seems it has no point since it doesn't involve a generic? i could be wrong. anyway it was causing a weird error)
  • Replaced the function body with todo!()

So this compiles:

fn main() {}

type Error = Box<dyn std::error::Error>;
type Result<R = ()> = std::result::Result<R, Error>;

pub struct Arena;

pub trait AsSized<'a> {
    type AsSized: Sized + 'a;
}
impl<'a, T: Sized + 'a> AsSized<'a> for T {
    type AsSized = Self;
}
impl<'a, T: AsSized<'a>> AsSized<'a> for [T] {
    type AsSized = &'a [T::AsSized];
}

pub trait Format<T>: Send + Sync
where
    T: ?Sized,
{
    fn get_bytes<'a>(&self, value: &'a T, arena: &'a Arena) -> Result<&'a [u8]>;
    fn get_value<'a>(&self, bytes: &'a [u8], arena: &'a Arena) -> Result<T::AsSized>
    where
        T: AsSized<'a>;
}

struct RawBytes;

impl Format<[u8]> for RawBytes {
    fn get_bytes<'a>(&self, value: &'a [u8], _arena: &'a Arena) -> Result<&'a [u8]> {
        Ok(value)
    }
    fn get_value<'a>(
        &self,
        bytes: &'a [u8],
        arena: &'a Arena,
    ) -> Result<<[u8] as AsSized<'a>>::AsSized>
    where
        [u8]: AsSized<'a>,
    {
        todo!()
    }
}

Unfortunately, as soon as I replace the todo!() back with your Ok(bytes), it breaks again. The compiler apparently doesn't know that <[u8] as AsSized<'a>>::AsSized is the same as &'a [u8]. Oh well.

error[E0308]: mismatched types
  --> src/main.rs:42:12
   |
42 |         Ok(bytes)
   |         -- ^^^^^ expected associated type, found `&[u8]`
   |         |
   |         arguments to this enum variant are incorrect
   |
   = note: expected associated type `<[u8] as AsSized<'a>>::AsSized`
                    found reference `&'a [u8]`
   = help: consider constraining the associated type `<[u8] as AsSized<'a>>::AsSized` to `&'a [u8]` or calling a method that returns `<[u8] as AsSized<'a>>::AsSized`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Reinis Mazeiks