如何测试特征对象之间的相等性?

Sam*_*uel 11 rust

编者注:此代码示例来自1.0之前的Rust版本,并且在语法上不是有效的Rust 1.0代码.此代码的更新版本会产生不同的错误,但答案仍包含有价值的信息.

在下列情况下,似乎我们无法测试相等性.为什么是这样?有解决方法吗?(我正在使用Rust 0.11).

trait A: PartialEq {}

#[deriving(PartialEq)]
enum T {Ta, Tb}

impl A for T {}

fn main() {
  assert!(Ta == Ta);
  assert!(Ta != Tb);
  assert!(some_fn(&Ta, &Ta));
  assert!(!some_fn(&Ta, &Tb));
}

fn some_fn(an_a: &A, another_a: &A) -> bool {
    an_a == another_a
// ERROR ^~~~~~~~~~~~ binary operation `==` cannot be applied to type `&A`
}

fn another_fn(an_a: &A + PartialEq, another_a: &A + PartialEq) -> bool {
               // ERROR: ^~~~~~~~~ only the builtin traits can be used as closure or object bounds
    an_a == another_a
}
Run Code Online (Sandbox Code Playgroud)

Vla*_*eev 11

以下是特征的定义PartialEq:

pub trait PartialEq<Rhs = Self> 
where
    Rhs: ?Sized, 
{
    fn eq(&self, other: &Rhs) -> bool;

    fn ne(&self, other: &Rhs) -> bool { ... }
}
Run Code Online (Sandbox Code Playgroud)

请注意Self参数类型.这意味着eq()ne()方法接受与实现者相同类型的参数.例如:

impl PartialEq for i32 {
    fn eq(&self, other: &i32) -> bool { ... }
}

impl PartialEq for String {
    fn eq(&self, other: &String) -> bool { ... }
}
Run Code Online (Sandbox Code Playgroud)

请注意如何实现other反映类型的更改类型PartialEq.

这就是问题.在特征对象中,实际类型在运行时被擦除且不可用.这意味着不可能从特征对象获得对具体类型的引用; 特别是,你不能去&A&T在你的榜样.

这意味着无法调用接受或返回Self特征对象类型的方法.实际上,这些方法总是需要一个具体的类型,但如果你只有一个特征对象,就没有具体的类型,并且这种方法无法以任何合理的方式工作.


She*_*ter 10

在 trait 对象的某些情况下,您希望基于通过 trait 公开的一些属性来比较它们。您可以通过在 trait 类型本身实现方法来实现这一点:

trait A {
    fn id(&self) -> i32;
}

impl PartialEq for dyn A + '_ {
    fn eq(&self, other: &Self) -> bool {
        self.id() == other.id()
    }
}

impl Eq for dyn A + '_ {}

fn some_fn(an_a: &dyn A, another_a: &dyn A) -> bool {
    an_a == another_a
}
Run Code Online (Sandbox Code Playgroud)

这并没有直接解决想要委托回PartialEq底层类型的实现的原始情况,但是您可以结合现有的解决方案

impl PartialEq for dyn A + '_ {
    fn eq(&self, other: &Self) -> bool {
        self.equals_a(other)
    }
}
Run Code Online (Sandbox Code Playgroud)

也可以看看:

  • 我很惊讶!经过大量的努力后,它非常简单地解决了我的问题。 (2认同)

Sam*_*uel 6

弗拉基米尔·马特维耶夫(Vladimir Matveev)的帮助下,我想出了如何使用Any将我的特质转向具体类型并测试结果值是否相等:

// `Any` allows us to do dynamic typecasting.
use std::any::Any;

trait A {
    // An &Any can be cast to a reference to a concrete type.
    fn as_any(&self) -> &dyn Any;

    // Perform the test.
    fn equals_a(&self, _: &dyn A) -> bool;
}

#[derive(Debug, PartialEq)]
enum T {
    Ta,
    Tb,
}

// Implement A for all 'static types implementing PartialEq.
impl<S: 'static + PartialEq> A for S {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn equals_a(&self, other: &dyn A) -> bool {
        // Do a type-safe casting. If the types are different,
        // return false, otherwise test the values for equality.
        other
            .as_any()
            .downcast_ref::<S>()
            .map_or(false, |a| self == a)
    }
}

fn main() {
    assert_eq!(T::Ta, T::Ta);
    assert_ne!(T::Ta, T::Tb);
    assert!(some_fn(&T::Ta, &T::Ta));
    assert!(!some_fn(&T::Ta, &T::Tb));
}

fn some_fn(an_a: &dyn A, another_a: &dyn A) -> bool {
    // It works!
    an_a.equals_a(another_a)
}
Run Code Online (Sandbox Code Playgroud)