by_address/
lib.rs

1//! Wrapper type for by-address hashing and comparison.
2//!
3//! [`ByAddress`] can be used to wrap any pointer type (i.e. any type that implements the Deref
4//! trait).  This includes references, raw pointers, smart pointers like `Rc<T>` and `Box<T>`, and
5//! specialized pointer-like types such as `Vec<T>` and `String`.
6//!
7//! Comparison, ordering, and hashing of the wrapped pointer will be based on the address of its
8//! contents, rather than their value.
9//!
10//! ```
11//! use by_address::ByAddress;
12//! use std::rc::Rc;
13//!
14//! let rc = Rc::new(5);
15//! let x = ByAddress(rc.clone());
16//! let y = ByAddress(rc.clone());
17//!
18//! // x and y are two pointers to the same address:
19//! assert_eq!(x, y);
20//!
21//! let z = ByAddress(Rc::new(5));
22//!
23//! // *x and *z have the same value, but not the same address:
24//! assert_ne!(x, z);
25//! ```
26//!
27//! If `T` is a pointer to an unsized type, then comparison of `ByAddress<T>` uses the
28//! entire fat pointer, not just the "thin" data address.  This means that two slice pointers
29//! are consider equal only if they have the same starting address *and* length.
30//!
31//! ```
32//! # use by_address::ByAddress;
33//! #
34//! let v = [1, 2, 3, 4];
35//!
36//! assert_eq!(ByAddress(&v[0..4]), ByAddress(&v[0..4])); // Same address and length.
37//! assert_ne!(ByAddress(&v[0..4]), ByAddress(&v[0..2])); // Same address, different length.
38//! ```
39//!
40//! You can use [`ByThinAddress`] instead if you want to compare slices by starting address only,
41//! or trait objects by data pointer only.
42//!
43//! You can use wrapped pointers as keys in hashed or ordered collections, like BTreeMap/BTreeSet
44//! or HashMap/HashSet, even if the target of the pointer doesn't implement hashing or ordering.
45//! This even includes pointers to trait objects, which usually don't implement the Eq trait
46//! because it is not object-safe.
47//!
48//! ```
49//! # use by_address::ByAddress;
50//! # use std::collections::HashSet;
51//! #
52//! /// Call each item in `callbacks`, skipping any duplicate references.
53//! fn call_each_once(callbacks: &[&dyn Fn()]) {
54//!     let mut seen: HashSet<ByAddress<&dyn Fn()>> = HashSet::new();
55//!     for &f in callbacks {
56//!         if seen.insert(ByAddress(f)) {
57//!             f();
58//!         }
59//!     }
60//! }
61//! ```
62//!
63//! However, note that comparing fat pointers to trait objects can be unreliable because of
64//! [Rust issue #46139](https://github.com/rust-lang/rust/issues/46139).  In some cases,
65//! [`ByThinAddress`] may be more useful.
66//!
67//! This crate does not depend on libstd, so it can be used in [`no_std`] projects.
68//!
69//! [`no_std`]: https://doc.rust-lang.org/book/first-edition/using-rust-without-the-standard-library.html
70
71// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
72// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
73// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
74// option. This file may not be copied, modified, or distributed
75// except according to those terms.
76
77#![no_std]
78
79use core::cmp::Ordering;
80use core::convert::AsRef;
81use core::fmt::{Debug, Display, Formatter};
82use core::hash::{Hash, Hasher};
83use core::ops::{Deref, DerefMut};
84use core::ptr;
85
86/// Wrapper for pointer types that implements by-address comparison.
87///
88/// See the [crate-level documentation](index.html) for details.
89///
90/// Equality tests and hashes on fat pointers (`&dyn Trait`, `&[T]`, `&str`, etc)
91/// include the attribute of the fat pointer.
92///
93/// However, note that comparing fat pointers to trait objects can be unreliable because of
94/// [Rust issue #46139](https://github.com/rust-lang/rust/issues/46139).  In some cases,
95/// [`ByThinAddress`] may be more useful.
96#[repr(transparent)]
97#[derive(Copy, Clone, Default)]
98pub struct ByAddress<T>(pub T)
99where
100    T: ?Sized + Deref;
101
102impl<T> ByAddress<T>
103where
104    T: ?Sized + Deref,
105{
106    /// Convenience method for pointer casts.
107    fn addr(&self) -> *const T::Target {
108        &*self.0
109    }
110
111    /// Convert `&T` to `&ByAddress<T>`.
112    pub fn from_ref(r: &T) -> &Self {
113        // SAFETY: `struct ByAddress` is `repr(transparent)`.
114        unsafe {
115            &*(r as *const T as *const Self)
116        }
117    }
118}
119
120struct DebugAdapter<'a, T>(&'a T)
121where
122    T: ?Sized + Deref + Debug;
123
124impl<'a, T> Debug for DebugAdapter<'a, T>
125where
126    T: ?Sized + Deref + Debug,
127{
128    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
129        self.0.fmt(f)?;
130        f.write_str(" @ ")?;
131        (self.0.deref() as *const T::Target).fmt(f)?;
132        Ok(())
133    }
134}
135
136impl<T> Debug for ByAddress<T>
137where
138    T: ?Sized + Deref + Debug,
139{
140    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
141        f.debug_tuple("ByAddress")
142            .field(&DebugAdapter(&self.0))
143            .finish()
144    }
145}
146
147impl<T> Display for ByAddress<T>
148where
149    T: ?Sized + Deref + Display,
150{
151    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
152        self.0.fmt(f)
153    }
154}
155
156/// Raw pointer equality
157impl<T> PartialEq for ByAddress<T>
158where
159    T: ?Sized + Deref,
160{
161    fn eq(&self, other: &Self) -> bool {
162        ptr::eq(self.addr(), other.addr())
163    }
164}
165impl<T> Eq for ByAddress<T> where T: ?Sized + Deref {}
166
167/// Raw pointer ordering
168impl<T> Ord for ByAddress<T>
169where
170    T: ?Sized + Deref,
171{
172    fn cmp(&self, other: &Self) -> Ordering {
173        self.addr().cmp(&other.addr())
174    }
175}
176
177/// Raw pointer comparison
178impl<T> PartialOrd for ByAddress<T>
179where
180    T: ?Sized + Deref,
181{
182    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
183        Some(self.addr().cmp(&other.addr()))
184    }
185}
186
187/// Raw pointer hashing
188impl<T> Hash for ByAddress<T>
189where
190    T: ?Sized + Deref,
191{
192    fn hash<H: Hasher>(&self, state: &mut H) {
193        self.addr().hash(state)
194    }
195}
196
197// Generic conversion traits:
198
199impl<T> Deref for ByAddress<T>
200where
201    T: ?Sized + Deref,
202{
203    type Target = T;
204
205    fn deref(&self) -> &Self::Target {
206        &self.0
207    }
208}
209
210impl<T> DerefMut for ByAddress<T>
211where
212    T: ?Sized + Deref,
213{
214    fn deref_mut(&mut self) -> &mut Self::Target {
215        &mut self.0
216    }
217}
218
219impl<T, U> AsRef<U> for ByAddress<T>
220where
221    T: ?Sized + Deref + AsRef<U>,
222{
223    fn as_ref(&self) -> &U {
224        self.0.as_ref()
225    }
226}
227
228impl<T, U> AsMut<U> for ByAddress<T>
229where
230    T: ?Sized + Deref + AsMut<U>,
231{
232    fn as_mut(&mut self) -> &mut U {
233        self.0.as_mut()
234    }
235}
236
237impl<T> From<T> for ByAddress<T>
238where
239    T: Deref,
240{
241    fn from(t: T) -> ByAddress<T> {
242        ByAddress(t)
243    }
244}
245
246/// Similar to [`ByAddress`], but omits the attributes of fat pointers.
247///
248/// This means that two slices with the same starting element but different lengths will be
249/// considered equal.
250///
251/// Two trait objects with the same data pointer but different vtables will also be considered
252/// equal.  (In particular, this may happen for traits that are implemented on zero-sized types,
253/// including `Fn` and other closure traits.)
254#[repr(transparent)]
255#[derive(Copy, Clone, Default)]
256pub struct ByThinAddress<T>(pub T)
257where
258    T: ?Sized + Deref;
259
260impl<T> ByThinAddress<T>
261where
262    T: ?Sized + Deref,
263{
264    /// Convenience method for pointer casts.
265    fn addr(&self) -> *const T::Target {
266        &*self.0
267    }
268
269    /// Convert `&T` to `&ByThinAddress<T>`.
270    pub fn from_ref(r: &T) -> &Self {
271        // SAFETY: `struct ByAddress` is `repr(transparent)`.
272        unsafe {
273            &*(r as *const T as *const Self)
274        }
275    }
276}
277
278impl<T> Debug for ByThinAddress<T>
279where
280    T: ?Sized + Deref + Debug,
281{
282    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
283        f.debug_tuple("ByThinAddress")
284            .field(&DebugAdapter(&self.0))
285            .finish()
286    }
287}
288
289impl<T> Display for ByThinAddress<T>
290where
291    T: ?Sized + Deref + Display,
292{
293    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
294        self.0.fmt(f)
295    }
296}
297
298/// Raw pointer equality
299impl<T> PartialEq for ByThinAddress<T>
300where
301    T: ?Sized + Deref,
302{
303    fn eq(&self, other: &Self) -> bool {
304        core::ptr::eq(self.addr() as *const (), other.addr() as *const _)
305    }
306}
307impl<T> Eq for ByThinAddress<T> where T: ?Sized + Deref {}
308
309/// Raw pointer ordering
310impl<T> Ord for ByThinAddress<T>
311where
312    T: ?Sized + Deref,
313{
314    fn cmp(&self, other: &Self) -> Ordering {
315        (self.addr() as *const ()).cmp(&(other.addr() as *const ()))
316    }
317}
318
319/// Raw pointer comparison
320impl<T> PartialOrd for ByThinAddress<T>
321where
322    T: ?Sized + Deref,
323{
324    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
325        Some((self.addr() as *const ()).cmp(&(other.addr() as *const ())))
326    }
327}
328
329/// Raw pointer hashing
330impl<T> Hash for ByThinAddress<T>
331where
332    T: ?Sized + Deref,
333{
334    fn hash<H: Hasher>(&self, state: &mut H) {
335        (self.addr() as *const ()).hash(state)
336    }
337}
338
339// Generic conversion traits:
340
341impl<T> Deref for ByThinAddress<T>
342where
343    T: ?Sized + Deref,
344{
345    type Target = T;
346
347    fn deref(&self) -> &Self::Target {
348        &self.0
349    }
350}
351
352impl<T> DerefMut for ByThinAddress<T>
353where
354    T: ?Sized + Deref,
355{
356    fn deref_mut(&mut self) -> &mut Self::Target {
357        &mut self.0
358    }
359}
360
361impl<T, U> AsRef<U> for ByThinAddress<T>
362where
363    T: ?Sized + Deref + AsRef<U>,
364{
365    fn as_ref(&self) -> &U {
366        self.0.as_ref()
367    }
368}
369
370#[cfg(test)]
371mod tests {
372    extern crate std;
373    use std::format;
374
375    use crate::{ByAddress, ByThinAddress};
376
377    trait A: std::fmt::Debug {
378        fn test(&self) {}
379    }
380    trait B: A {
381        fn test2(&self) {}
382    }
383
384    #[derive(Debug)]
385    struct Test {}
386    impl A for Test {}
387    impl B for Test {}
388
389    fn force_vtable<O: B>(v: &O) -> &dyn A {
390        v
391    }
392
393    #[test]
394    fn test_thin_ptr_fail() {
395        let t = Test {};
396        let tr1: &dyn A = &t;
397        let tr2: &dyn A = force_vtable(&t);
398
399        let a = ByAddress(tr1);
400        let b = ByAddress(tr2);
401
402        assert_ne!(a, b);
403    }
404
405    #[test]
406    fn test_thin_ptr_success() {
407        let t = Test {};
408        let tr1: &dyn A = &t;
409        let tr2: &dyn A = force_vtable(&t);
410
411        let a = ByThinAddress(tr1);
412        let b = ByThinAddress(tr2);
413
414        assert_eq!(a, b);
415    }
416
417    #[test]
418    fn test_debug() {
419        let x = &1;
420        let b = ByAddress(x);
421        let expected = format!("ByAddress(1 @ {:p})", x);
422        let actual = format!("{:?}", b);
423        assert_eq!(expected, actual);
424
425        let t = ByThinAddress(x);
426        let expected = format!("ByThinAddress(1 @ {:p})", x);
427        let actual = format!("{:?}", t);
428        assert_eq!(expected, actual);
429    }
430}