问题描述
我正在研究和试验铁锈。 我希望实现一个抽象训练“ 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>`
有什么主意要纠正吗?
解决方法
尝试修复错误
您有两个错误:
- 第一个是易于解决的问题:
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
特性的对象,但是由于对象的类型为GraphEntity
,所以它仅具有dyn GraphEntity
的知识。 对于大多数特征,您可以给GraphEntity
赋予一个超级特征,而这个特征是固定的,但是不幸的是,这在这里不起作用,因为您无法为Ord
创建特征对象。您会收到错误E0038,因为这可能是不安全的。 我的一个小建议是在cmp
中创建一个GraphEntity
函数,并使用binary_search_by
代替binary_search
。但是cmp
将具有一个Self
参数,这将导致相同的错误。据我所知,您无法按照自己的方式进行修复。
如果您无法在Rust中修复代码,则代码逻辑经常出问题。我认为情况就是这样,因为对于binary_search
,您需要比较dyn GraphEntity
对象。因此,您还想将Point
与Triangle
(没有(部分)顺序关系)进行比较。
使用枚举
您可能会注意到,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初学者都犯的错误,包括我自己。