TryFrom<&[u8]> trait bound in trait

Viewed 910

I'm trying to implement common trait for a bunch of types created from binary data (read from a disk). Majority of trait methods could use default implementations and only conversions etc. would be needed to be implemented separately. I would like to use TryFrom<&[u8]> trait for conversions from binary data to my types but I don't know how to express (in the context of trait) that lifetime of &[u8] and lifetimes of values of my types created from it are not related. Here is minimal example of the problem.

use std::convert::TryFrom;

struct Foo;

// Value of Foo can be created from &[u8] but it doesn't borrow anything.
impl TryFrom<&[u8]> for Foo {
    type Error = ();

    fn try_from(v: &[u8]) -> Result<Self, ()> {
        Ok(Foo)
    }
}

trait Bar<'a>
where
    Self: TryFrom<&'a [u8], Error = ()>, // `&` without an explicit lifetime name cannot be used here
{
    fn baz() -> Self {
        let vec = Vec::new();
        Self::try_from(&vec).unwrap() // ERROR: vec does not live long enough (nothing is borrowed)
    }
}

Alternative solution would be to make conversions as trait methods but it would be nicer to use common std traits. Is there a way to achieve this? (Or I could use const generics but I don't want to rely on nightly compiler.)

1 Answers

What you want are "higher ranked trait bounds" (HRTB, or simply hearty boy). They look like this: for<'a> T: 'a. This example just means: "for every possible lifetime 'a, T must ...". In your case:

trait Bar
where
    Self: for<'a> TryFrom<&'a [u8], Error = ()>,

You can also specify that requirement as super trait bound directly instead of where clause:

trait Bar: for<'a> TryFrom<&'a [u8], Error = ()> { ... }

And yes, now it just means that all implementors of Bar have to implement TryFrom<&'a [u8], Error = ()> for all possible lifetimes. That's what you want.

Working Playground

Related