【问题标题】:'inheritance' of generic trait implementation通用特征实现的“继承”
【发布时间】:2015-03-23 23:53:30
【问题描述】:

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

这是我想出的测试代码(注意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 是通用实现的可行候选者,因为它还没有将 trait 与它相关联。上面的代码产生以下错误:

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

如何将特质与我的类型联系起来? 是否有任何替代方法可以实现这一目标而不是实际实现该特征?

我的结论

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

经验教训是,泛型系统中的 特征 被用作标记来选择(并因此限制)您想要应用泛型实现的类型集。

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

然后可以使用批准的答案中看到的通用特征实现自动使特征可用于匹配通用边界的类型。

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

【问题讨论】:

    标签: rust


    【解决方案1】:

    问题是,到最后一天左右(由于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());
    }
    

    【讨论】:

    • 您的解决方案甚至比我在研究迭代器实现时发现的解决方案还要好。非常感谢您的快速回复!
    猜你喜欢
    • 1970-01-01
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多