Как проверить равенство между объектами признаков?
Примечание редактора: Этот пример кода относится к версии 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
}
Ответы
Ответ 1
С помощью Владимира Матвеева я понял, как использовать 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)
}
Ответ 2
Вот определение признака 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
для объектов объектов. Действительно, эти методы всегда требуют конкретного типа, но если у вас есть только объект-признак, то нет конкретного типа, и нет никакого способа, чтобы такой метод мог работать каким-либо разумным способом.
Ответ 3
В определенных случаях объектов признаков вы хотите сравнить их, основываясь на некоторых свойствах, представленных через свойство. Этого можно добиться, реализовав методы для самого типа черты:
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
}
Это напрямую не касается исходного случая, который хочет делегировать обратно реализации PartialEq
базового типа, но вы можете объединить существующее решение:
impl PartialEq for dyn A + '_ {
fn eq(&self, other: &Self) -> bool {
self.equals_a(other)
}
}