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(data:" @ ")?;
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&mut DebugTuple<'_, '_>.debug_tuple(name:"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(), b: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&mut DebugTuple<'_, '_>.debug_tuple(name:"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 (), b: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}
431