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 | |
79 | use core::cmp::Ordering; |
80 | use core::convert::AsRef; |
81 | use core::fmt::{Debug, Display, Formatter}; |
82 | use core::hash::{Hash, Hasher}; |
83 | use core::ops::{Deref, DerefMut}; |
84 | use 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)] |
98 | pub struct ByAddress<T>(pub T) |
99 | where |
100 | T: ?Sized + Deref; |
101 | |
102 | impl<T> ByAddress<T> |
103 | where |
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 | |
120 | struct DebugAdapter<'a, T>(&'a T) |
121 | where |
122 | T: ?Sized + Deref + Debug; |
123 | |
124 | impl<'a, T> Debug for DebugAdapter<'a, T> |
125 | where |
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 | |
136 | impl<T> Debug for ByAddress<T> |
137 | where |
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 | |
147 | impl<T> Display for ByAddress<T> |
148 | where |
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 |
157 | impl<T> PartialEq for ByAddress<T> |
158 | where |
159 | T: ?Sized + Deref, |
160 | { |
161 | fn eq(&self, other: &Self) -> bool { |
162 | ptr::eq(self.addr(), b:other.addr()) |
163 | } |
164 | } |
165 | impl<T> Eq for ByAddress<T> where T: ?Sized + Deref {} |
166 | |
167 | /// Raw pointer ordering |
168 | impl<T> Ord for ByAddress<T> |
169 | where |
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 |
178 | impl<T> PartialOrd for ByAddress<T> |
179 | where |
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 |
188 | impl<T> Hash for ByAddress<T> |
189 | where |
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 | |
199 | impl<T> Deref for ByAddress<T> |
200 | where |
201 | T: ?Sized + Deref, |
202 | { |
203 | type Target = T; |
204 | |
205 | fn deref(&self) -> &Self::Target { |
206 | &self.0 |
207 | } |
208 | } |
209 | |
210 | impl<T> DerefMut for ByAddress<T> |
211 | where |
212 | T: ?Sized + Deref, |
213 | { |
214 | fn deref_mut(&mut self) -> &mut Self::Target { |
215 | &mut self.0 |
216 | } |
217 | } |
218 | |
219 | impl<T, U> AsRef<U> for ByAddress<T> |
220 | where |
221 | T: ?Sized + Deref + AsRef<U>, |
222 | { |
223 | fn as_ref(&self) -> &U { |
224 | self.0.as_ref() |
225 | } |
226 | } |
227 | |
228 | impl<T, U> AsMut<U> for ByAddress<T> |
229 | where |
230 | T: ?Sized + Deref + AsMut<U>, |
231 | { |
232 | fn as_mut(&mut self) -> &mut U { |
233 | self.0.as_mut() |
234 | } |
235 | } |
236 | |
237 | impl<T> From<T> for ByAddress<T> |
238 | where |
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)] |
256 | pub struct ByThinAddress<T>(pub T) |
257 | where |
258 | T: ?Sized + Deref; |
259 | |
260 | impl<T> ByThinAddress<T> |
261 | where |
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 | |
278 | impl<T> Debug for ByThinAddress<T> |
279 | where |
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 | |
289 | impl<T> Display for ByThinAddress<T> |
290 | where |
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 |
299 | impl<T> PartialEq for ByThinAddress<T> |
300 | where |
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 | } |
307 | impl<T> Eq for ByThinAddress<T> where T: ?Sized + Deref {} |
308 | |
309 | /// Raw pointer ordering |
310 | impl<T> Ord for ByThinAddress<T> |
311 | where |
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 |
320 | impl<T> PartialOrd for ByThinAddress<T> |
321 | where |
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 |
330 | impl<T> Hash for ByThinAddress<T> |
331 | where |
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 | |
341 | impl<T> Deref for ByThinAddress<T> |
342 | where |
343 | T: ?Sized + Deref, |
344 | { |
345 | type Target = T; |
346 | |
347 | fn deref(&self) -> &Self::Target { |
348 | &self.0 |
349 | } |
350 | } |
351 | |
352 | impl<T> DerefMut for ByThinAddress<T> |
353 | where |
354 | T: ?Sized + Deref, |
355 | { |
356 | fn deref_mut(&mut self) -> &mut Self::Target { |
357 | &mut self.0 |
358 | } |
359 | } |
360 | |
361 | impl<T, U> AsRef<U> for ByThinAddress<T> |
362 | where |
363 | T: ?Sized + Deref + AsRef<U>, |
364 | { |
365 | fn as_ref(&self) -> &U { |
366 | self.0.as_ref() |
367 | } |
368 | } |
369 | |
370 | #[cfg (test)] |
371 | mod 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 | |