Расширенные черты в коллекциях

У меня есть простая черта Fruit и расширенная черта WeightedFruit, Компилятор Rust принимает Fruit черта в LinkedList но нет WeightedFruit в BTreeSet, Что нужно изменить, чтобы отсортированный набор работал?

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

1 ответ

Решение
pub trait WeightedFruit: Fruit + Ord { }

Это говорит о каждой структуре, которая реализует WeightedFruit должен быть сопоставим с собой. Но не для других структур, которые реализуют эту черту. Так что если Apple инвентарь WeightedFruit, это будет сопоставимо с Apple, если Orange инвентарь WeightedFruit, это будет сопоставимо с Orangeно не друг другу.

Вы не можете создать коллекцию "всего, что является 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 можно сравнить с любым другим WeightedFruit используя это weight, Теперь вы можете сделать черты объектов из WeightedFruit и смешать их вместе в коллекциях, потому что они взаимозаменяемы.


Дополнительное объяснение о Ord и the trait ... cannot be made into an object ошибка:

Когда вы работаете с объектами признаков, признаки выглядят как интерфейсы в ОО-языках. Вы можете иметь s trait и несколько структур, которые его реализуют, и функцию, которая принимает объект trait. Затем он может вызывать функции признака объекта, поскольку знает, что он будет иметь их, и что они абсолютно одинаковы для каждого объекта. Прямо как в ОО-языках.

Однако у черт есть еще одна особенность: они могут использовать Self введите объявления функции. Self всегда тот тип, который реализует черту. Если черта использует Self в любой из своих функций он становится особенным и больше не может использоваться как объект черты. Каждый раз, когда структура реализует такую ​​черту, она реализует другую версию (версия, где Self это отличается). Вы не можете создать объект-черту, потому что каждая структура, которая его реализует, реализует свою версию.

Ord в ржавчине это как Comparable<T> в Яве, где T выбран для вас компилятором. И так же, как вы не можете иметь метод, который принимает что-либо Comparable в Java (надеюсь, вы не можете?), вы не можете иметь метод, который принимает любой Ord черта объекта.

Другие вопросы по тегам