2

I'm trying to define a trait for an object that is convertible to and from a slice of bytes. I essentially want to say

trait Foo: AsRef<[u8]> + TryFrom<&[u8]> {}

Unfortunately, this refuses to compile unless I put a lifetime parameter on the reference, like so:

trait Foo<'a>: AsRef<[u8]> + TryFrom<&'a [u8]> {}

This doesn't make much sense to me, because the lifetime 'a is related to the eventual try_from() call and shouldn't have to be part of the object's type. (The implementation of try_from() copies the relevant bytes, so the lifetime of its parameter really isn't relevant.)

This seems like a more general issue than just slices, though; how do you specify lifetime parameters like this for supertrait bounds? (Apparently '_ doesn't work.) And is there a better/more idiomatic way to express this, or do I have to resort to some sort of hand-rolled custom nonsense like

pub trait TryFromRef<T> { type Error; fn try_from(value: &T) -> Result<Self, Self::Error>; }

?

Reid Rankin
  • 1,078
  • 8
  • 26
  • 1
    There must be a duplicate for this one, I'll look for it. The answer is to use a *higher-ranked trait bound*: `trait Foo: AsRef<[u8]> + for<'a> TryFrom<&'a [u8]> {}` – trent Apr 10 '20 at 01:48
  • @trentcl Genius. I remember reading about those in the Book, but didn't connect it with this issue. Stick it in an answer and I'll accept – Reid Rankin Apr 10 '20 at 01:50
  • Yeah, the other questions I was thinking of aren't exactly what you're looking for IMO. I'll write an answer. – trent Apr 10 '20 at 01:56

1 Answers1

2

A trait bound with a lifetime parameter that holds for all lifetimes, rather than for some particular lifetime, can be specified with a so-called higher-ranked trait bound, or HRTB. In your case this might look like

trait Foo: AsRef<[u8]> + for<'a> TryFrom<&'a [u8]> {}

Anything implementing Foo must satisfy TryFrom<&'a u8> for any and all choices of 'a, so there's no need for a lifetime on Foo itself.

See also

trent
  • 25,033
  • 7
  • 51
  • 90