Default implementation of supertrait

Viewed 1072

I have a trait, MyGoodTrait, with the function label(&self) -> &str. I want every implementor of MyGoodTrait to also implement Display and FromStr. However, I do not necessarily need Display and FromStr to be supertraits of MyGoodTrait. I would rather somehow have a default implementation of Display and FromStr, which will internally use the label function from MyGoodTrait. That way, every implementor of MyGoodTrait will get Display and FromStr "for free", as if there was a default implementation for those traits.

Here is an example that is similar to what I want to do, but it does not compile:

use std::str::FromStr;

pub trait MyGoodTrait {
    fn new() -> Self;

    fn label(&self) -> &'static str;
}

impl FromStr for dyn MyGoodTrait {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Self::new())
    }
}

pub struct A {}

impl MyGoodTrait for A {

    fn new() -> Self {
        A{}
    }
    fn label(&self) -> &'static str {
        "A"
    }
}

pub struct B {}

impl MyGoodTrait for B {

    fn new() -> Self {
        B{}
    }
    fn label(&self) -> &'static str {
        "B"
    }
}

// In this hypothetical, A and B now both have `fmt` and `from_str` functions

Is there a way to write this default implementation of Display and FromStr, such that I do not have to duplicate the code for each struct that implements MyGoodTrait?

Note: My actual use case is that I have a trait which has serde::se::Serialize and serde::de::Deserialize as supertraits. The implementors of my trait will be used as keys in a map, and I will serialize the map to JSON, so I need the implementors to be serialized to Strings. So this may be an example of the XY Problem

1 Answers

TL;DR: You can't.


You can't implement FromStr for dyn SomeTrait because it has a method that returns a Result<Self, _>, hence you can only implement it for types whose size are know at compile time, which is not the case of trait objects.

What you would really want is

impl<T: MyGoodTrait> FromStr for T

But now you might violate the orphan rule. As the compiler explains:

Implementing a foreign trait is only possible if at least one of the types for which is it implemented is local. Only traits defined in the current crate can be implemented for a type parameter.

But you could do it if FromStr was a local trait instead:

/// Copy of `std::str::FromStr`
trait Foo: Sized {
    type Err;

    fn from_str(s: &str) -> Result<Self, Self::Err>;
}

impl<T: MyGoodTrait> Foo for T {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Self::new())
    }
}

Or you could implement it for any specific local type:

impl FromStr for A {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Self::new())
    }
}
Related