对特征执行 Eq 检查

Enforce Eq check on trait

假设有以下对象:

pub struct MyStruct<T>{
  items: Vec<T>
}

pub impl<T> MyStruct<T> {
  pub fn new() -> MyStruct {
     MyStruct{ items::new(); }
  }
  pub fn add(&mut self, item: T) where T : Eq {
    self.items.push(item);
  }
}

pub trait C {}

pub struct Another {
  mystruct: MyStruct<Box<C>>
}

pub impl Another {
  pub fn exec<C>(&mut self, c: C) where C: Eq + C {
     self.mystruct.add(c);
  }
}

在 exec 上,我强制 C 也是 Eq,但我收到以下错误:

 error: the trait `core::cmp::Eq` is not implemented for the type `C`

我不得不

pub impl<T> MyStruct<T> 

而不是

pub impl<T : Eq> MyStruct<T> 

因为 C 是一个特性,所以我在使用 MyStruct::new 时无法强制执行 Eq,所以我在函数上保留了类型保护检查。这里发生了什么?

我们来看看Eq的相关定义:

pub trait Eq: PartialEq<Self> {
    …
}

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

现在考虑MyStruct<Box<C>>:它要实现的类型EqBox<C>,一个装箱的特征对象。要实现EqBox<C>必须先实现PartialEq<Box<C>>,像这样:

impl PartialEq for Box<C> {
    fn eq(&self, other: &Box<C>) -> bool;
}

impl Eq for Box<C> { }

也就是说,您必须能够将任意 Box<C> 与任何其他任意 Box<C> 进行比较。您正在比较的盒装特征对象可以是不同的具体类型,在这里。因此,您需要手动编写此实现。在这种情况下,您通常希望特征包含某种将对象的形式规范化为具体的、可比较的类型的方法;对于某些类型,这是显而易见的;如果 AsRef<T> 要添加 PartialEq 实现,要添加的实现将相当明显:

impl<T: PartialEq> PartialEq for AsRef<T> {
    fn eq(&self, other: &AsRef<T>) -> bool {
        self.as_ref() == other.as_ref()
    }
}

(另请注意,如果 C 实现了 PartialEqBox<C> 实现了,那么我们正在讨论的此类实现应该在未装箱的特征对象上进行,而不是在装箱的特征上对象。)

然而,通常不是明显而简单的实现。您可以采取以下几种方法:

  1. 将对象(可能成本高,但理想情况下成本低)转换为某种基本类型,例如一个 String 然后可以比较;

  2. 放弃;

  3. C 限制为 'static 类型并使用一些奇特的 Any 魔法使其使用基本类型的 PartialEq 实现,如果两个特征对象不是同一具体类型,则返回 false。这是一个相当有用的,所以我会给出一些代码:

    #![feature(core)]
    
    use std::any::{Any, TypeId};
    use std::mem;
    
    fn main() { }
    
    trait PartialEqFromC {
        fn eq_c(&self, other: &C) -> bool;
    }
    
    impl<T: PartialEq + Any + C> PartialEqFromC for T {
        fn eq_c(&self, other: &C) -> bool {
            if other.get_type_id() == TypeId::of::<Self>() {
                self == unsafe { *mem::transmute::<&&C, &&Self>(&other) }
            } else {
                false
            }
        }
    }
    
    trait C: Any + PartialEqFromC {
    }
    
    impl PartialEq for C {
        fn eq(&self, other: &C) -> bool {
            self.eq_c(other)
        }
    }
    

    请注意,此示例依赖于 Any.get_type_id 的不稳定功能 core,因此仅适用于夜间;这可以通过将 Any 特征的定义复制到 C 的新超特征中来解决,也可以通过 mopafying the C trait.

  4. 来简化