Skip to main content

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}