我遇到的情况是,我的几个 struct 应该实现多个特性,但它们都至少实现了一个共同的特性.当我得到这些 struct 的混合包时,我想把它们都视为共同特征:将它们作为键入该特征的方法参数传递,存储在为该特征键入的集合中,等等.

我还不知道怎么做.下面是一些代码,我try 按照这里建议的方式执行,但未能编译:

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实现.

推荐答案

Rust 的性状遗传不同于OOP遗传.trait 继承只是一种指定需求的方式.trait B: A does not imply如果一个类型实现B,它将自动实现A;这意味着,如果一个类型实现B,那么它将实现must implement A.这也意味着,如果实现了B,则必须实现A separately.

例如,

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

但是,如果希望一个类型在实现AB时自动实现C(从而避免为该类型手动实现C),则可以使用泛型impl:

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

在你的例子中,这意味着

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

更多信息,请查看this forum thread.

顺便说一句,PianoWithKeys不需要ThingWithKeys,因为Piano已经需要ThingWithKeys.

EDIT(根据您的 comments 和问题编辑):

如前所述,trait inheritance in Rust differs from OOP inheritance.即使是trait B: A,也不能将B的特征对象强制为A的特征对象.如果您别无 Select ,只能按原样将trait对象传递给方法,那么使用泛型就可以了:

fn use_keys<T: ThingWithKeys + ?Sized>(thing_with_keys: &T) {
    println! ("{}", thing_with_keys.use_keys ());
}

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

Also check: Why doesn't Rust support trait object upcasting?

Rust相关问答推荐

使用InlineTables序列化toml ArrayOfTables

给定使用newype习语定义的类型上的铁 rust Vec,有没有方法获得底层原始类型的一部分?

为什么复印是豆荚的一个重要特征?

MacOS(AARCH64)上Ghidra中的二进制补丁导致进程终止

将大小为零的类型实例存储到空指针中

异步FN中的 rust 递归

如何正确重新排列代码以绕过铁 rust 借入判断器?

有没有可能让泛型Rust T总是堆分配的?

我们能确定Rust会优化掉Clone()吗?如果它会立即掉落?

考虑到Rust不允许多个可变引用,类似PyTorch的自动区分如何在Rust中工作?

在 Rust 中,在需要引用 self 的 struct 体方法中使用闭包作为 while 循环条件

如何执行数组文字的编译时串联?

这个不安全的 Rust 代码有什么问题,所以它可以在 Windows 上运行,但不能在 Ubuntu 上运行?

如何限制 GtkColumnView 行数

Nom 解析器无法消耗无效输入

通过mem::transmute将数组展平安全吗?

‘&T as *const T as *mut T’ 在 ‘static mut’ 项目中合适吗?

rust tokio::spawn 在 mutexguard 之后等待

为什么指定生命周期让我返回一个引用?

令人困惑的错误消息? (解包运算符)