dyn_eq/lib.rs
1// Copyright (c) 2023 Zacharie Dubrulle
2// This Source Code Form is subject to the terms of the Mozilla Public
3// License, v. 2.0. If a copy of the MPL was not distributed with this
4// file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
6//! [![github]](https://github.com/Rayzeq/dyn-eq)
7//! [![crates.io]](https://crates.io/crates/dyn-eq)
8//! [![license]](https://www.mozilla.org/en-US/MPL/2.0/)
9//! [![passively-maintained]](https://github.com/Rayzeq/dyn-eq/issues)
10//!
11//! [github]: https://img.shields.io/badge/github-rayzeq/dyn--eq-a?style=for-the-badge&logo=github
12//! [crates.io]: https://img.shields.io/crates/v/dyn-eq?style=for-the-badge&logo=rust
13//! [license]: https://img.shields.io/crates/l/dyn-eq?style=for-the-badge
14//! [passively-maintained]: https://img.shields.io/badge/maintenance-passively--maintained-brightgreen?style=for-the-badge
15//!
16//! This crate provides a [`DynEq`] trait which permit comparing trait objects.
17//! If the two objects are instances of different structs, they will always be
18//! not equal. If they are instances of the same struct, the struct's [`Eq`]
19//! will be used.
20//!
21//! ###### Todos
22//!
23//! Here's a list of things that could be done and could be nice to have, but I'll implement them only if someone ask:
24//! - [ ] Permit having `PartialEq` without `Eq` (implementation on `dyn Trait` will follow)
25//!
26//! # Features
27//!
28//! This crate has one feature: `alloc`, which is enabled by default. Disabling
29//! this feature removes the dependency on the [`alloc`] crate, but you won't be
30//! able to use [`DynEq`] for `Box<dyn Trait>`.
31//!
32//! [`alloc`]: https://doc.rust-lang.org/alloc/
33//!
34//! # Example
35//!
36//! ```
37//! use dyn_eq::DynEq;
38//!
39//! trait MyTrait: DynEq {}
40//! dyn_eq::eq_trait_object!(MyTrait);
41//!
42//! impl MyTrait for u8 {}
43//! impl MyTrait for u16 {}
44//!
45//! let a: &dyn MyTrait = &5u8;
46//! let a_bis: &dyn MyTrait = &5u8;
47//! let b: &dyn MyTrait = &10u8;
48//! let c: &dyn MyTrait = &5u16;
49//! let d: &dyn MyTrait = &10u16;
50//!
51//! // Same type, same value
52//! assert!(a == a_bis);
53//! // Same type, different value
54//! assert!(a != b);
55//! // Different type, different value
56//! assert!(a != d);
57//! // Different type, same value
58//! // Even if the value is the same, the fact that it's a diffrent type means it's not equal
59//! assert!(a != c);
60//!
61//! // Now data structures containing Box<dyn MyTrait> can derive Eq (only when `alloc`
62//! // feature is enabled).
63//! # #[cfg(feature = "alloc")]
64//! #[derive(PartialEq, Eq)]
65//! struct Container {
66//! field: Box<dyn MyTrait>
67//! }
68//! ```
69#![no_std]
70
71#[cfg(feature = "alloc")]
72extern crate alloc;
73
74/// Re-export of [`alloc::boxed::Box`] for the macro.
75#[cfg(feature = "alloc")]
76#[doc(hidden)]
77pub use alloc::boxed::Box;
78use core::any::Any;
79
80mod macros;
81
82/// This trait is implemented by any type that implements [`Eq`].
83pub trait DynEq: Any + private::Sealed {
84 /// Upcast this reference to a `&dyn Any`, which can then be passed to [`dyn_eq`](DynEq::dyn_eq).
85 #[doc(hidden)]
86 fn as_any(&self) -> &dyn Any;
87
88 /// This method tests for self and other values to be equal.
89 #[doc(hidden)]
90 fn dyn_eq(&self, other: &dyn Any) -> bool;
91}
92
93impl<T: Eq + 'static> DynEq for T {
94 fn as_any(&self) -> &dyn Any {
95 self
96 }
97
98 fn dyn_eq(&self, other: &dyn Any) -> bool {
99 other.downcast_ref().map_or(false, |other| self == other)
100 }
101}
102
103/// Private module to seal the [`DynEq`] trait.
104mod private {
105 /// Sealing trait.
106 pub trait Sealed {}
107 impl<T> Sealed for T where T: PartialEq {}
108}