Lifetime in impl does not match method in trait
Asked Answered
T

1

12

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.

Tiein answered 26/7, 2020 at 2:26 Comment(3)
It's hard to answer your question because it doesn't include a minimal reproducible example. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. In the future, try to reproduce your error on the Rust Playground if possible, otherwise in a brand new Cargo project, then edit your question to include the additional info. There are Rust-specific MRE tips you can use to reduce your original code for posting here. Thanks!Frothy
Thanks for the tip! I didn't know there was a guide for this, should have checked the tag. I will edit the question.Tiein
I added a playground link that reproduces the exact same error I am getting and added a note about the version of Rust I am using.Tiein
A
3

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
Ambrosia answered 8/5, 2022 at 12:47 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.