Rust中的“Subclassing”特征

时间:2017-12-25 05:36:27

标签: rust polymorphism traits

我有一种情况,我的几个结构应该实现多个特征,但它们都实现了至少一个共同特征。当我抓住这些结构的混合包时,我希望将它们视为共同特征:将它们作为键入该特征的方法参数传递,将它们存储在为该特征键入的集合中等等。

我无法弄明白该怎么做。这里有一些代码,我尝试按照这里的方式进行操作,但无法编译:

trait ThingWithKeys {
    fn use_keys (&self) -> String;
}

//////

trait CorrectionsOfficer {
    fn hitch_up_pants (&self) -> String;
}

trait CorrectionsOfficerWithKeys: ThingWithKeys + CorrectionsOfficer {}

struct CorrectionsOfficerReal {}

impl ThingWithKeys for CorrectionsOfficerReal {
    fn use_keys (&self) -> String {
        String::from ("Clank, clank")
    }
}

impl CorrectionsOfficer for CorrectionsOfficerReal {
    fn hitch_up_pants (&self) -> String {
        String::from ("Grunt")
    }
}

impl <T: ThingWithKeys + CorrectionsOfficer> CorrectionsOfficerWithKeys for T {}

//////

trait Piano {
    fn close_lid (&self) -> String;
}

trait PianoWithKeys: Piano + ThingWithKeys {}

struct PianoReal {}

impl ThingWithKeys for PianoReal {
    fn use_keys (&self) -> String {
        String::from ("Tinkle, tinkle")
    }
}

impl Piano for PianoReal {
    fn close_lid (&self) -> String {
        String::from ("Bang!")
    }
}

impl <T: ThingWithKeys + Piano> PianoWithKeys for T {}

//////

trait Florida {
    fn hurricane (&self) -> String;
}

trait FloridaWithKeys: ThingWithKeys + Florida {}

struct FloridaReal {}

impl ThingWithKeys for FloridaReal {
    fn use_keys (&self) -> String {
        String::from ("Another margarita, please")
    }
}

impl Florida for FloridaReal {
    fn hurricane (&self) -> String {
        String::from ("Ho-hum...")
    }
}

impl <T: ThingWithKeys + Florida> FloridaWithKeys for T {}

//////

fn main() {
    let corrections_officer_ref: &CorrectionsOfficerWithKeys = &CorrectionsOfficerReal {};
    let piano_ref: &PianoWithKeys = &PianoReal {};
    let florida_ref: &FloridaWithKeys = &FloridaReal {};

    use_keys (corrections_officer_ref);
    use_keys (piano_ref);
    use_keys (florida_ref);
}

fn use_keys (thing_with_keys: &ThingWithKeys) {
    println! ("{}", thing_with_keys.use_keys ());
}

以下是编译错误:

Compiling playground v0.0.1 (file:///playground)
error[E0308]: mismatched types
  --> src/main.rs:80:19
   |
80 |         use_keys (corrections_officer_ref);
   |                   ^^^^^^^^^^^^^^^^^^^^^^^ expected trait `ThingWithKeys`, found trait `CorrectionsOfficerWithKeys`
   |
   = note: expected type `&ThingWithKeys`
              found type `&CorrectionsOfficerWithKeys`

error[E0308]: mismatched types
  --> src/main.rs:81:19
   |
81 |         use_keys (piano_ref);
   |                   ^^^^^^^^^ expected trait `ThingWithKeys`, found trait `PianoWithKeys`
   |
   = note: expected type `&ThingWithKeys`
              found type `&PianoWithKeys`

error[E0308]: mismatched types
  --> src/main.rs:82:19
   |
82 |         use_keys (florida_ref);
   |                   ^^^^^^^^^^^ expected trait `ThingWithKeys`, found trait `FloridaWithKeys`
   |
   = note: expected type `&ThingWithKeys`
              found type `&FloridaWithKeys`

error: aborting due to 3 previous errors

基本上,它仍然无法在XxxWithKeys实现中找到ThingWithKeys实现。

1 个答案:

答案 0 :(得分:6)

Rust中的特征继承与OOP继承不同。特征继承只是指定需求的一种方式。 trait B: A 并不暗示如果某个类型实现B,它将自动实现A;这意味着如果类型实现B,则必须实现 A。这也意味着,如果实施A,您必须单独实施B

举个例子,

trait A {}
trait B: A {}

struct S;

impl B for S {}

// Commenting this line will result in a "trait bound unsatisfied" error
impl A for S {}

fn main() {
    let _x: &B = &S;
}

但是,如果想要一个类型来自动实现C,如果它实现AB(从而避免为该类型手动实现C),那么你可以使用通用impl

impl<T: A + B> C for T {}

在您的示例中,这转换为

impl<T: Florida + ThingWithKeys> FloridaWithKeys for T {}

请查看this forum thread了解详情。

顺便说一句,由于ThingWithKeys已经需要PianoWithKeys,因此Piano不需要ThingWithKeys绑定。

编辑(根据您的评论和问题编辑):

如前所述,Rust中的 trait继承与OOP继承不同。即使trait B: A,也不能将B的特征对象强制转换为{{1}的特征对象。 1}}。如果除了将特征对象传递给方法之外别无选择,则使用泛型工作:

A

泛型方法也适用于类型引用(非特征对象)。

同时检查:Why doesn't Rust support trait object upcasting?