'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 theimpl
(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 |