'inheritance' 的通用特征实现

'inheritance' of generic trait implementation

我想尝试通用地实现一个特征,并让该特征的用户自动继承这个 'base' 实现,只要它们兼容。

这是我想出的测试代码(注意 fmt::Showstd::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();

但是,rust 不知道 MyType 是通用实现的可行候选者,因为它还没有将特征与其相关联。上面的代码产生以下错误:

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

如何将特征与我的类型相关联? 是否有任何替代方案来实现这一目标而不是实际实施该特征?

我的结论

批准的答案显然是实现这一点的绝对正确方法。为了完整起见,我展示了我同时想出的代码,这也教会了我如何修改特征。

经验教训是,通用系统中的 traits 像标记一样用于 select(并因此限制)要应用通用实现的类型集到.

如果你想将你的接口与使用此类接口的通用实现分开,特性修正很有用,它应该自动提供给任何实现你特性的人。

然后可以使用已批准答案中的通用特征实现来自动使特征可用于匹配通用范围的类型。

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 {}

问题是,到最后一天左右(由于 RFC 565),它不再被称为 Show。您需要使用 Debug 代替:

#![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());
}