Generic supertrait / requiring blanket impl

Viewed 189

Currently, I have a trait like this:

trait Trait
where
    Self: Sized,
{
    fn try_form_u8(num: u8) -> Option<Self>;
    fn try_from_str<T: AsRef<str>>(s: &T) -> Option<Self>;
    // ...
}

Optimally, I want to define it like simliar to this, in order to not have try_from_* methods outside of a TryFrom implementation:

trait Trait: for<T: AsRef<str>> TryFrom<u8> + TryFrom<T>
where
    Self: Sized
{
    // ...
}

I couldn't find a way to accomplish that and finally came across this thread: Can a trait have a supertrait that is parameterized by a generic?

I wonder how to continue from here. Should I use the unconventional try_from_str method or is there a better way to express what is needed here?

Note that in my original code I have Path instead of str, which could eliminate some special solutions, but I would still like to know them if there are any nice solution!

1 Answers

If a type implements TryFrom<&str>, it is trivial to create it from a String or Rc<str> or anything else that is AsRef<str>. So let's start with that:

trait Trait: TryFrom<u8> + for<'a> TryFrom<&'a str> {}

It's easy to use this with other types simply by calling .as_ref(), but if you still want the generic version, you can provide a default implementation:

trait Trait: TryFrom<u8> + for<'a> TryFrom<&'a str> {
    fn try_from_str<S: AsRef<str>>(arg: &S) -> Result<Self, <Self as TryFrom<&str>>::Error> {
        arg.as_ref().try_into()
    }
}

This way, implementors of Trait don't have to write anything special, but users still have the convenient try_from_str function.

Path version

trait Trait: TryFrom<u8> + for<'a> TryFrom<&'a Path> {
    fn try_from_path<P: AsRef<Path>>(arg: &P) -> Result<Self, <Self as TryFrom<&Path>>::Error> {
        arg.as_ref().try_into()
    }
}

str version

For str specifically, you might want to use the standard library trait FromStr instead of TryFrom, which is more specialized to this use case, enables str::parse and does not require a higher-ranked trait bound:

trait Trait: TryFrom<u8> + FromStr {
    fn try_from_str<S: AsRef<str>>(arg: &S) -> Result<Self, <Self as FromStr>::Err> {
        arg.as_ref().parse()
    }
}
Related