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

时间:2014-08-16 11:13:55

标签: 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
}

3 个答案:

答案 0 :(得分:11)

以下是PartialEq特征的定义:

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

    fn ne(&self, other: &Rhs) -> bool { ... }
}

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

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

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

请注意other的类型如何更改以反映为PartialEq实施的类型。

这就是问题所在。在特征对象中,实际类型已擦除并且在运行时不可用。这意味着不可能从特征对象获得对具体类型的引用;特别是,您的示例中不能从&A转到&T

这意味着无法调用在特征对象上接受或返回Self类型的方法。实际上,这些方法总是需要具体类型,但是如果你只有一个特征对象,那么没有具体类型,并且这种方法无法在任何方法中起作用明智的方式。

答案 1 :(得分:5)

使用help from 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) -> &Any;

    // Perform the test.
    fn equals_a(&self, &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) -> &Any {
        self
    }

    fn equals_a(&self, other: &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: &A, another_a: &A) -> bool {
    // It works!
    an_a.equals_a(another_a)
}

答案 2 :(得分:3)

在某些特质对象的情况下,您希望根据通过特征暴露的某些属性来比较它们。您可以通过在特征类型本身上实现方法来实现这一目的:

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

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

impl<'a> Eq for A + 'a {}

fn some_fn(an_a: &A, another_a: &A) -> bool {
    an_a == another_a
}

这并不能直接解决原来需要委托给基础类型的PartialEq实现的案例,但是你可以将Creating a Pandas DataFrame with a numpy array containing multiple types组合起来:

impl<'a> PartialEq for A + 'a {
    fn eq(&self, other: &Self) -> bool {
        self.equals_a(other)
    }
}