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

标签 rust

我正在研究和试验使用rust 。
我想实现一个抽象的火车“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,
    y: 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.x, 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>`
知道如何纠正这个问题吗?

最佳答案

试图修复错误
你有两个错误:

  • 第一个问题很容易解决:expected struct 'std::boxed::Box', found type parameter 'S' . binary_search函数从 self.list 开始需要一个框是 Vec的盒子。您可以将其更改为 self.list.binary_search(&Box::new(entity)) . (您可能仍然需要帮助编译器将其强制为 dyn 类型。)
  • 第二个错误有点棘手,很难解决。问题是 binary_search需要实现 Ord 的对象trait,但它只知道 GraphEntity因为对象的类型是dyn GraphEntity .
    对于大多数特征,您可以给出 GraphEntity一个 super 特征,这将被修复,但不幸的是,这在这里不起作用,因为您无法为 Ord 创建特征对象.你会得到错误 E0038 ,因为这可能不安全。
    我的小修复想法是创建一个 cmp GraphEntity中的函数并使用 binary_search_by而不是 binary_search .然而cmp会有 Self参数,这会导致相同的错误。据我所知,你无法按照你想要的方式修复它。

  • 如果您无法在 Rust 中修复您的代码,那么代码的逻辑通常会出现问题。我认为这里就是这种情况,因为对于 binary_search你需要比较dyn GraphEntity对象。所以你还想比较 PointTriangle ,它们没有(部分)顺序关系。
    使用枚举
    您可能会注意到,Rust 的动态调度不是为(某些)复杂情况而设计的。比较不同类型的对象并非易事。实现相同的特征并没有说明比较它们。
    如果 binary_search超过 GraphEntity objects 正是您想要的,而不是最好的解决方案可能是使用枚举。 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 初学者都会犯的错误,包括我自己。

    关于rust - 在带有 rust 的向量中实现特征的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64169075/

    相关文章:

    generics - 是否可以在Rust中编写通用整数长度函数?

    rust - 将数组从 Rust 返回到 FFI

    rust - 无法重新借用变量,因为我不能将不可变的局部变量借用为可变的

    c++ - 在 Rust 中捕获循环计数器拷贝的函数

    Rust 具有相同基本结构的不同返回类型

    rust - 不稳定库功能 "core"问题的解决方法是什么?

    rust - 有没有其他方法可以接受整数或 float 输入而不是接受字符串输入并进行转换?

    rust - 作为 Option<&str> 返回本地字符串

    file - 如何使用带有 BufReader 的文件并且仍然能够写入它?

    concurrency - 共享 self 引用时无法推断出合适的生命周期