Byron
Byron

Reputation: 4316

'inheritance' of generic trait implementation

I wanted to try implementing a trait generically and have users of the trait inherit this 'base' implementation automatically as long as they are compatible.

This is the test-code I came up with (note that fmt::Show is std::fmt::Show):

trait Outspoken {
    fn speak(&self) -> String;
}

impl<T: fmt::Show> Outspoken for T {
    fn speak(&self) -> String {
        format!("{:?}", self)
    }
}

// In theory, we can now let my-types speak
#[derive(Show)]
struct MyType(i32);

// 'Show' works
assert_eq!(format!("{:?}", MyType(15)), "MyType(15)");
// speak() however, doesn't
let mti = MyType(20);
mti.speak();

However, rust doesn't know that MyType is a viable candidate for the generic implementation as it didn't associate the trait with it yet. The code above produces the following errors:

tests/lang.rs:523:9: 523:16 error: type `generics_and_traits::MyType` does not implement any method in scope named `speak` tests/lang.rs:523 mti.speak(); ^~~~~~~ tests/lang.rs:523:16: 523:16 help: methods from traits can only be called if the trait is implemented and in scope; the following trait defines a method `speak`, perhaps you need to implement it: tests/lang.rs:523:16: 523:16 help: candidate #1: `generics_and_traits::Outspoken` error: aborting due to previous error

How can I associate the trait with my type ? Are there any alternatives to achieve that instead of actually implementing the trait ?

My Conclusion

The approved answer clearly is the absolute right way to implement this. Just for completeness, I am showing the code I came up with in the meanwhile, which also taught me how traits can be amended to.

Lesson learned is that traits in the generic system are used like markers to select (and thus restrict) the set of types you want to apply the generic implementation to.

Trait amendments are useful if you want to separate your interface from generic implementation that use such interface, which should automatically be made available to anyone who implements your trait.

Generic trait implementations as seen in the approved answer can then be used to automatically make traits available to types matching the generic bound.

trait Outspoken : fmt::Debug {};

trait Outspoken : fmt::Debug {};

// This defines a default implementation to any Trait. : Outspoken is any bound
trait OutspokenImpl : Outspoken {
    fn speak(&self) -> String {
        format!("{:?}", self)
    }
}

// This line tells the generics system to provide the implementation to all types
// which are outspoken
impl<T> OutspokenImpl for T where T: Outspoken {}

#[derive(Debug)]
struct MyType(i32);

// Add Outspoken marker to my type
impl Outspoken for MyType {};


assert_eq!(format!("{:?}", MyType(15)), "MyType(15)");
let mti = MyType(20);
assert_eq!(mti.speak(), "MyType(20)");

// You can bark even though the implementation follows later.
// Makes sense as we handle generics at compile time
assert_eq!(mti.bark(), "wuff");

// Add your own methods to any existing type who is Outspoken
trait AmendDoggyness : Outspoken {
    fn bark(&self) -> &str {
        "wuff"
    }
}

impl<T> AmendDoggyness for T where T: Outspoken {}any bound
trait OutspokenImpl : Outspoken {
    fn speak(&self) -> String {
        format!("{:?}", self)
    }
}

// This line tells the generics system to provide the implementation to all types
// which are outspoken
impl<T> OutspokenImpl for T where T: Outspoken {}

#[derive(Debug)]
struct MyType(i32);

// Add Outspoken marker to my type
impl Outspoken for MyType {};


assert_eq!(format!("{:?}", MyType(15)), "MyType(15)");
let mti = MyType(20);
assert_eq!(mti.speak(), "MyType(20)");

// You can bark even though the implementation follows later.
// Makes sense as we handle generics at compile time
assert_eq!(mti.bark(), "wuff");

// Add your own methods to any existing type who is Outspoken
trait AmendDoggyness : Outspoken {
    fn bark(&self) -> &str {
        "wuff"
    }
}

impl<T> AmendDoggyness for T where T: Outspoken {}

Upvotes: 1

Views: 3583

Answers (1)

DK.
DK.

Reputation: 58975

The problem is that, as of the last day or so (due to RFC 565), it's not called Show any more. You need to use Debug instead:

#![allow(unstable)]
use std::borrow::ToOwned;
use std::fmt::Debug;

trait Outspoken {
    fn speak(&self) -> String;
}

impl<T> Outspoken for T where T: Debug {
    fn speak(&self) -> String {
        format!("{:?}", self)
    }
}

#[derive(Debug)]
struct MyType(i32);

fn main() {
    assert_eq!(format!("{:?}", MyType(15)), "MyType(15)");
    assert_eq!(MyType(20).speak(), "MyType(20)".to_owned());
}

Upvotes: 6

Related Questions