tract_data/dyn_eq.rs
1//! Equality between trait objects.
2//!
3//! A trait with [`DynEq`] as a supertrait can have `PartialEq` and `Eq`
4//! implemented for its objects by [`eq_trait_object!`]. Objects of two
5//! different concrete types are never equal; two of the same type compare with
6//! that type's own `Eq`.
7//!
8//! Import [`DynEq`] in the modules that call `dyn_eq` and nowhere else — do not
9//! re-export it from a prelude. The blanket impl also covers `Box<dyn Trait>`,
10//! whose `dyn_eq` downcasts to the box and so answers false for every
11//! comparison. Method resolution reaches that impl only where the trait is in
12//! scope, so a wide re-export silently turns `boxed.dyn_eq(other)` from a
13//! comparison of the pointees into a constant false.
14
15use std::any::Any;
16
17/// Type-erased equality, blanket-implemented for every `Eq + 'static` type.
18///
19/// Add it as a supertrait, then invoke [`eq_trait_object!`] on the trait — the
20/// two go together, the trait alone gives objects nothing.
21pub trait DynEq: Any {
22 /// Upcast, so the comparison has something to downcast back from.
23 #[doc(hidden)]
24 fn as_any(&self) -> &dyn Any;
25
26 /// True if `other` is the same concrete type as `self` and equal to it.
27 #[doc(hidden)]
28 fn dyn_eq(&self, other: &dyn Any) -> bool;
29}
30
31impl<T: Eq + 'static> DynEq for T {
32 fn as_any(&self) -> &dyn Any {
33 self
34 }
35
36 fn dyn_eq(&self, other: &dyn Any) -> bool {
37 other.downcast_ref::<T>().is_some_and(|other| self == other)
38 }
39}
40
41pub use crate::eq_trait_object;
42
43/// Implement `PartialEq` and `Eq` for the objects of a trait having [`DynEq`]
44/// as a supertrait, in all four `Send`/`Sync` combinations.
45///
46/// Takes a bare trait name: a generic trait would need the argument list
47/// threaded through, and tract has none needing this.
48///
49/// ```
50/// use tract_data::dyn_eq::DynEq;
51///
52/// trait Weigh: DynEq {}
53/// tract_data::eq_trait_object!(Weigh);
54///
55/// impl Weigh for u8 {}
56/// impl Weigh for u16 {}
57///
58/// let five: &dyn Weigh = &5u8;
59/// assert!(five == &5u8 as &dyn Weigh);
60/// assert!(five != &6u8 as &dyn Weigh);
61/// // same value, other type: not equal
62/// assert!(five != &5u16 as &dyn Weigh);
63/// ```
64#[macro_export]
65macro_rules! eq_trait_object {
66 ($trait:ident) => {
67 $crate::__eq_trait_object!($trait,);
68 $crate::__eq_trait_object!($trait, + ::core::marker::Send);
69 $crate::__eq_trait_object!($trait, + ::core::marker::Sync);
70 $crate::__eq_trait_object!($trait, + ::core::marker::Send + ::core::marker::Sync);
71 };
72}
73
74#[doc(hidden)]
75#[macro_export]
76macro_rules! __eq_trait_object {
77 ($trait:ident, $($auto:tt)*) => {
78 impl<'eq> ::core::cmp::PartialEq for dyn $trait $($auto)* + 'eq {
79 fn eq(&self, other: &Self) -> bool {
80 $crate::dyn_eq::DynEq::dyn_eq(self, $crate::dyn_eq::DynEq::as_any(other))
81 }
82 }
83
84 impl<'eq> ::core::cmp::Eq for dyn $trait $($auto)* + 'eq {}
85
86 impl<'eq> ::core::cmp::PartialEq<&Self> for ::std::boxed::Box<dyn $trait $($auto)* + 'eq> {
87 fn eq(&self, other: &&Self) -> bool {
88 self == *other
89 }
90 }
91 };
92}