在带有锈的向量中实现特征的问题

问题描述

我正在研究和试验铁锈。 我希望实现一个抽象训练“ GraphEntity”,然后实现另外两个特征“ Point”和“ Triangle”。 我有下面的代码可以正常工作:

use std::cmp::Ordering;

trait GraphEntity {
    fn plot (&self) {
        println!("this is a plot!");
    }
}

#[derive(Debug)]
struct Point {
    x: i32,y: i32,}

impl Ord for Point {
    fn cmp(&self,other: &Self) -> Ordering {
        (self.x,&self.y).cmp(&(other.x,&other.y))
    }
}

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

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

impl Eq for Point { }


#[derive(Debug)]
struct Triangle {
    x: i32,z: i32,}


impl GraphEntity for Point {
    fn plot (&self) {
        println!("P:[{},{}]",self.x,self.y);
    }

}

impl GraphEntity for Triangle {
    fn plot (&self) {
        println!("T:[{},{},self.y,self.z);
    }

}

struct SetofEntities {
       list: Vec<Box<dyn GraphEntity>>,}

impl SetofEntities {
    fn new () -> Self {
        Self {
            list: Vec::new(),}
    }

    fn append<S: GraphEntity + 'static>(&mut self,entity: S) -> &mut Self {
        self.list.push(Box::new(entity));
        
        self
    }
    
    fn plot(self) {
        for x in self.list {
            x.plot();
        }
    }
}


fn main() {

    let p1: Point = Point { x: 1,y: 0 };
    let p2: Point = Point { x: -1,y: 2 };
    let t: Triangle = Triangle { x: 1,y: 2,z: 3};

    let mut set = SetofEntities::new();
    set.append(p1);
    set.append(p2);
    set.append(t);
    set.plot();
}

我现在想更改附加函数并检查列表中是否已存在任何现有点。 如果已经存在,则不会添加到列表中。

fn append<S: GraphEntity + 'static>(&mut self,entity: S) -> &mut Self {
    match self.list.binary_search(&entity) {
        Ok(pos) => self,Err(pos) => { self.list.push(Box::new(entity)); self },}
}

这给了我以下错误

error[E0308]: mismatched types
  --> src/main.rs:70:39
   |
69 |     fn append<S: GraphEntity + 'static>(&mut self,entity: S) -> &mut Self {
   |               - this type parameter
70 |         match self.list.binary_search(&entity) {
   |                                       ^^^^^^^ expected struct `std::Boxed::Box`,found type parameter `S`
   |
   = note: expected reference `&std::Boxed::Box<dyn GraphEntity>`
              found reference `&S`

error[E0277]: the trait bound `dyn GraphEntity: std::cmp::Ord` is not satisfied
  --> src/main.rs:70:39
   |
70 |         match self.list.binary_search(&entity) {
   |                                       ^^^^^^^ the trait `std::cmp::Ord` is not implemented for `dyn GraphEntity`
   |
   = note: required because of the requirements on the impl of `std::cmp::Ord` for `std::Boxed::Box<dyn GraphEntity>`

有什么主意要纠正吗?

解决方法

尝试修复错误

您有两个错误:

  1. 第一个是易于解决的问题:expected struct 'std::boxed::Box',found type parameter 'S'binary_search函数需要一个框,因为self.listVec个框。您可以将其更改为self.list.binary_search(&Box::new(entity))。 (您可能仍需要帮助编译器将其强制为dyn类型。)
  2. 第二个错误比较棘手,很难解决。问题是binary_search需要实现Ord特性的对象,但是由于对象的类型为GraphEntity,所以它仅具有dyn GraphEntity的知识。 对于大多数特征,您可以给GraphEntity赋予一个超级特征,而这个特征是固定的,但是不幸的是,这在这里不起作用,因为您无法为Ord创建特征对象。您会收到错误E0038,因为这可能是不安全的。 我的一个小建议是在cmp中创建一个GraphEntity函数,并使用binary_search_by代替binary_search。但是cmp将具有一个Self参数,这将导致相同的错误。据我所知,您无法按照自己的方式进行修复。

如果您无法在Rust中修复代码,则代码逻辑经常出问题。我认为情况就是这样,因为对于binary_search,您需要比较dyn GraphEntity对象。因此,您还想将PointTriangle(没有(部分)顺序关系)进行比较。

使用枚举

您可能会注意到,Rust的动态调度不是针对(某些)复杂情况而进行的。比较不同类型的对象并非易事。实施相同的特征并不能说明如何比较它们。

如果binary_search上的GraphEntity对象正是您想要的对象,那么最好的解决方案可能是使用枚举。 Rust将确保您处理比较不同类型的所有情况。 比较功能可能如下所示:

fn cmp(&self,other: &GraphEntity) -> Ordering {
    match (self,other) {
        (GraphEntity::Point(p1),GraphEntity::Point(p2))
            => (p1.x,p1.y).cmp(&(p2.x,p2.y)),(GraphEntity::Triangle(t1),GraphEntity::Triangle(t2))
            => (t1.x,t1.y,t1.z).cmp(&(t2.x,t2.y,t2.z)),(GraphEntity::Point(_),GraphEntity::Triangle(_))
            => Ordering::Less,(GraphEntity::Triangle(_),GraphEntity::Point(_))
            => Ordering::Greater,}
}

使用GraphEntity枚举中的更多变体,这可能会变得非常复杂,但是可以处理复杂性。动态调度在处理模块化方面更好。

似乎您试图像在基于继承的OOP语言(如Java或C ++)中那样对程序进行建模。请注意这一点,这是很多Rust初学者都犯的错误,包括我自己。