same/
lib.rs

1//! This crate provides mainly the trait `Some` which can be used on *shared*
2//! reference types. (`&T`, `Rc`, `Arc`). It enables the users to test identity
3//! of objects. It's analogous to `PartialEq`, which tests *equality* instead.
4//! 
5//! Additionally, this crate provides `RefHash` trait, which is used for hashing
6//! references and `RefCmp` wrapper struct, which implements `Eq`, `PartialEq`
7//! and `Hash` by delegating to `Same` and `RefHash` traits. This is mainly
8//! useful if one wants to store objects in `HashSet` or similar data structure,
9//! with.
10//! 
11//! This crate is `no_std`-compatible.
12
13#![no_std]
14
15#[cfg(feature = "std")]
16extern crate std;
17
18use core::hash::{Hash, Hasher};
19
20/// Allows to test identity of objects.
21///
22/// # Example:
23///
24/// ```
25/// use same::Same;
26///
27/// let a = 42;
28/// let b = 42;
29/// let a_ref0 = &a;
30/// let a_ref1 = &a;
31/// let b_ref = &b;
32///
33/// // `a_ref0` and `a_ref1` point to the same object...
34/// assert!(a_ref0.same(&a_ref1));
35/// // ... but `a_ref0` and `b_ref` don't...
36/// assert!(!a_ref0.same(&b_ref));
37/// // ... neither do `a_ref1` and `b_ref`.
38/// assert!(!a_ref1.same(&b_ref));
39/// ```
40///
41/// This trait is currently implemented for *shared* references,
42/// `Rc` and `Arc`.
43///
44/// Note that it doesn't make sense to implement this trait for mutable
45/// references, nor boxes because there can never be two of them pointing
46/// to the same address, so the implementation would always return `false`.
47pub trait Same {
48    /// Returns true if `self` is the same instance of object as `other`.
49    fn same(&self, other: &Self) -> bool;
50}
51
52/// Hashes the pointer to the object instead of the object itself.
53///
54/// This trait works exatly like `Hash`, the only difference being
55/// that it hashes the pointer.
56pub trait RefHash {
57    /// Feeds the value into the hasher.
58    fn ref_hash<H: Hasher>(&self, hasher: &mut H);
59}
60
61impl<'a, T> Same for &'a T {
62    fn same(&self, other: &Self) -> bool {
63        let a: *const T = *self;
64        let b: *const T = *other;
65        
66        a == b
67    }
68}
69
70#[cfg(feature = "std")]
71impl<T> Same for std::rc::Rc<T> {
72    fn same(&self, other: &Self) -> bool {
73        std::rc::Rc::ptr_eq(self, other)
74    }
75}
76
77#[cfg(feature = "std")]
78impl<T> Same for std::sync::Arc<T> {
79    fn same(&self, other: &Self) -> bool {
80        std::sync::Arc::ptr_eq(self, other)
81    }
82}
83
84impl<'a, T> RefHash for &'a T {
85    fn ref_hash<H: Hasher>(&self, hasher: &mut H) {
86        let ptr: *const T = *self;
87
88        ptr.hash(hasher);
89    }
90}
91
92#[cfg(feature = "std")]
93impl<T> RefHash for std::rc::Rc<T> {
94    fn ref_hash<H: Hasher>(&self, hasher: &mut H) {
95        (&**self).ref_hash(hasher);
96    }
97}
98
99#[cfg(feature = "std")]
100impl<T> RefHash for std::sync::Arc<T> {
101    fn ref_hash<H: Hasher>(&self, hasher: &mut H) {
102        (&**self).ref_hash(hasher);
103    }
104}
105
106/// Wrapper for types to make their equality operations compare pointers.
107///
108/// This wrapper turns `Same` into `PartialEq` and `RefHash` into `Hash`.
109/// It is mainly useful for storing unique objects in hash sets and similar
110/// data structures.
111///
112/// # Example
113/// ```
114/// use same::RefCmp;
115///
116/// use std::rc::Rc;
117/// use std::collections::HashSet;
118///
119/// let a = ::std::sync::Arc::new(42);
120/// let a_cloned = a.clone();
121/// let b = ::std::sync::Arc::new(42);
122///
123/// let mut hash_set = ::std::collections::HashSet::new();
124/// assert!(hash_set.insert(RefCmp(a)));
125/// // a_cloned points to the same object as a...
126/// assert!(!hash_set.insert(RefCmp(a_cloned)));
127/// // but `b` doesn't, even though it has same value.
128/// assert!(hash_set.insert(RefCmp(b)));
129/// ```
130pub struct RefCmp<T: Same>(pub T);
131
132impl<T: Same> Eq for RefCmp<T> {}
133impl<T: Same> PartialEq for RefCmp<T> {
134    fn eq(&self, other: &Self) -> bool {
135        self.0.same(&other.0)
136    }
137}
138
139impl<T: Same + RefHash> Hash for RefCmp<T> {
140    fn hash<H: Hasher>(&self, hasher: &mut H) {
141        self.0.ref_hash(hasher);
142    }
143}
144
145impl<T: Same> core::ops::Deref for RefCmp<T> {
146    type Target = T;
147
148    fn deref(&self) -> &Self::Target {
149        &self.0
150    }
151}
152
153impl<U, T: Same + AsRef<U>> AsRef<U> for RefCmp<T> {
154    fn as_ref(&self) -> &U {
155        self.0.as_ref()
156    }
157}
158
159impl<T: Same> core::borrow::Borrow<T> for RefCmp<T> {
160    fn borrow(&self) -> &T {
161        &self.0
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use ::Same;
168    use ::RefCmp;
169
170    #[test]
171    fn refs() {
172        let a = 42;
173        let b = 42;
174
175        let a_ref = &a;
176        let a_ref_again = &a;
177        let b_ref = &b;
178
179        assert!(a_ref.same(&a_ref_again));
180        assert!(!a_ref.same(&b_ref));
181
182        let mut hash_set = ::std::collections::HashSet::new();
183        assert!(hash_set.insert(RefCmp(a_ref)));
184        assert!(!hash_set.insert(RefCmp(a_ref_again)));
185        assert!(hash_set.insert(RefCmp(b_ref)));
186    }
187
188    #[test]
189    fn rcs() {
190        let a = ::std::rc::Rc::new(42);
191        let a_cloned = a.clone();
192        let b = ::std::rc::Rc::new(42);
193
194        assert!(a.same(&a_cloned));
195        assert!(!a.same(&b));
196
197        let mut hash_set = ::std::collections::HashSet::new();
198        assert!(hash_set.insert(RefCmp(a)));
199        assert!(!hash_set.insert(RefCmp(a_cloned)));
200        assert!(hash_set.insert(RefCmp(b)));
201    }
202
203    #[test]
204    fn arcs() {
205        let a = ::std::sync::Arc::new(42);
206        let a_cloned = a.clone();
207        let b = ::std::sync::Arc::new(42);
208
209        assert!(a.same(&a_cloned));
210        assert!(!a.same(&b));
211
212        let mut hash_set = ::std::collections::HashSet::new();
213        assert!(hash_set.insert(RefCmp(a)));
214        assert!(!hash_set.insert(RefCmp(a_cloned)));
215        assert!(hash_set.insert(RefCmp(b)));
216    }
217}