Как проверить на равенство между чертами объектов?

Примечание редактора. Этот пример кода относится к версии Rust до 1.0 и не является синтаксически верным кодом 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 ответа

Решение

С помощью Владимира Матвеева я разобрался, как пользоваться 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)
}

Вот определение 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 печатать на чертах объектов. Действительно, эти методы всегда требуют конкретного типа, но если у вас есть только объект-черта, нет конкретного типа, и нет способа, которым такой метод мог бы работать любым разумным способом.

В некоторых случаях объектов признаков вы хотите сравнить их, основываясь на некоторых свойствах, представленных через свойство. Вы можете достичь этого, реализовав методы для самого типа черты:

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 базового типа, но вы можете объединить существующее решение:

impl<'a> PartialEq for A + 'a {
    fn eq(&self, other: &Self) -> bool {
        self.equals_a(other)
    }
}
Другие вопросы по тегам