集合中的扩展特征

Extended Traits in Collections

我有一个普通特征 Fruit 和一个扩展特征 WeightedFruit。 Rust 编译器接受 LinkedList 中的 Fruit 特征,但不接受 BTreeSet 中的 WeightedFruit。应该更改什么以使排序集工作?

pub trait Fruit { }

pub trait WeightedFruit: Fruit + Ord { }

pub fn main() {
    let unsorted: LinkedList<Box<Fruit>> = LinkedList::new();
    let sorted: BTreeSet<Box<WeightedFruit>> = BTreeSet::new();
}

错误信息是:

the trait `WeightedFruit` cannot be made into an object
trait `WeightedFruit: std::cmp::Ord` not satisfied
...
pub trait WeightedFruit: Fruit + Ord { }

这表示实现 WeightedFruit 的每个结构都必须与自身相比较。但不适用于实现该特征的其他结构。因此,如果 Apple 实现 WeightedFruit,它将与 Apple 相媲美,如果 Orange 实现 WeightedFruit,它将与 Orange 相媲美, 但不是彼此。

您不能构建 "anything that is WeightedFruit" 的集合,因为它们不可互换 - 苹果和橙子不同,因为它们都对应不同的种类。

相反,您想做这样的事情:

use std::cmp::*;
use std::collections::*;

pub trait Fruit { }

pub trait WeightedFruit: Fruit {
    fn weight(&self) -> u32;
}

impl Ord for WeightedFruit {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight().cmp(&other.weight())
    }
}

impl PartialOrd for WeightedFruit {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for WeightedFruit {
    fn eq(&self, other: &Self) -> bool {
        self.weight() == other.weight()
    }
}

impl Eq for WeightedFruit {}


struct Apple {
    weight: u32
}

impl Fruit for Apple {}

impl WeightedFruit for Apple {
    fn weight(&self) -> u32 {
        self.weight
    }
}

struct Orange {
    weight: u32
}

impl Fruit for Orange {}

impl WeightedFruit for Orange {
    fn weight(&self) -> u32 {
        self.weight
    }
}

pub fn main() {
    let unsorted: LinkedList<Box<Fruit>> = LinkedList::new();
    let sorted: BTreeSet<Box<WeightedFruit>> = BTreeSet::new();
}

这表示每个 WeightedFruit 水果必须能够提供其 weight 并且每个 WeightedFruit 都可以使用此 [=20= 与任何其他 WeightedFruit 进行比较].现在您可以创建 WeightedFruit 的特征对象并将它们混合在集合中,因为它们可以互换。


关于 Ordthe trait ... cannot be made into an object 错误的补充说明:

当您使用特征对象时,特征看起来有点像 OO 语言中的接口。你可以有 s trait 和多个实现它的结构,以及一个接受 trait 对象的函数。然后它可以在对象上调用特征的函数,因为它知道它将拥有它们并且它们对于每个对象都是完全相同的。就像在 OO 语言中一样。

然而 traits 有一项额外的功能:它们可以在函数声明中使用 Self 类型。 Self 始终是实现特征的类型。如果特征在其任何函数中使用 Self ,它就会变得特殊并且不能再用作特征对象。每次结构实现这种特征时,它都会实现它的不同版本(Self 不同的版本)。您不能创建特征对象,因为实现它的每个结构都在实现它的不同版本。

rust 中的 Ord 就像 java 中的 Comparable<T>,其中 T 是由编译器为您选择的。就像你不能有一个方法接受 java 中的任何东西 Comparable (我希望你不能?),你不能有一个方法接受任何 Ord 特征对象。