blueprint_cmp_any/
eq.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is dual-licensed under either the MIT license found in the
5 * LICENSE-MIT file in the root directory of this source tree or the Apache
6 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
7 * of this source tree. You may select, at your option, one of the
8 * above-listed licenses.
9 */
10
11use std::any::Any;
12use std::any::TypeId;
13use std::marker::PhantomData;
14
15/// A comparable "token" that can be returned to wrap a reference to an [`Any`
16/// type](Any) for [`PartialEq`](PartialEq).
17///
18/// This lets dyn traits be comparable by having all implementations return some
19/// "token" that can be considered [`PartialEq`](PartialEq).
20pub struct PartialEqAny<'a> {
21    type_id: TypeId,
22    cmp: unsafe fn(*const (), *const ()) -> bool,
23    val: *const (),
24    _marker: PhantomData<&'a dyn Any>,
25}
26
27impl<'a> PartialEqAny<'a> {
28    #[inline]
29    pub fn new<A: PartialEq + 'static>(a: &'a A) -> Self {
30        PartialEqAny {
31            type_id: TypeId::of::<A>(),
32            cmp: |this, other| {
33                // SAFETY: We only call `cmp` with
34                //   `this.type_id == other.type_id == TypeId::of::<A>()`.
35                let this = unsafe { &*(this as *const A) };
36                let other = unsafe { &*(other as *const A) };
37                this == other
38            },
39            val: a as *const A as *const (),
40            _marker: PhantomData,
41        }
42    }
43
44    /// Get `TypeId` of the referenced type.
45    #[inline]
46    pub fn type_id(&self) -> TypeId {
47        self.type_id
48    }
49
50    /// gets an instance that always compares to false
51    #[inline]
52    pub fn always_false() -> Self {
53        struct AlwaysFalse;
54
55        impl PartialEq for AlwaysFalse {
56            fn eq(&self, _other: &Self) -> bool {
57                false
58            }
59        }
60
61        PartialEqAny::new(&AlwaysFalse)
62    }
63}
64
65impl<'a> PartialEq for PartialEqAny<'a> {
66    #[inline]
67    fn eq(&self, other: &PartialEqAny<'a>) -> bool {
68        self.type_id == other.type_id && unsafe { (self.cmp)(self.val, other.val) }
69    }
70}
71
72#[cfg(test)]
73#[allow(clippy::bool_assert_comparison)]
74mod tests {
75    use crate::eq::PartialEqAny;
76
77    struct Wrap<T>(T);
78
79    impl<T: PartialEq + 'static> Wrap<T> {
80        fn token(&self) -> PartialEqAny<'_> {
81            PartialEqAny::new(&self.0)
82        }
83    }
84
85    #[test]
86    fn test_cmp_any() {
87        let w1 = Wrap(1);
88        let w2 = Wrap(1);
89        let w3 = Wrap(2);
90
91        assert_eq!(w1.token() == w2.token(), true);
92        assert_eq!(w1.token() == w3.token(), false);
93
94        let w4 = Wrap("foo");
95        let w5 = Wrap("foo");
96        let w6 = Wrap("bar");
97
98        assert_eq!(w4.token() == w5.token(), true);
99        assert_eq!(w4.token() == w6.token(), false);
100
101        assert_eq!(w1.token() == w6.token(), false);
102    }
103
104    #[test]
105    #[allow(clippy::eq_op)]
106    fn always_false_cmp() {
107        let w = Wrap(1);
108        let f = PartialEqAny::always_false();
109
110        assert_eq!(f == f, false);
111        assert_eq!(f == w.token(), false);
112    }
113}