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: &[&Fn()]) { |
54 | //! let mut seen: HashSet<ByAddress<&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 | |
85 | /// Wrapper for pointer types that implements by-address comparison. |
86 | /// |
87 | /// See the [crate-level documentation](index.html) for details. |
88 | /// |
89 | /// Note that equality tests and hashes on fat pointers (`&dyn Trait`, `&[T]`, `&str`, etc) |
90 | /// include the attribute of the fat pointer. If this is not desired, use [`ByThinAddress`]. |
91 | #[derive (Copy, Clone, Default)] |
92 | pub struct ByAddress<T>(pub T) |
93 | where |
94 | T: ?Sized + Deref; |
95 | |
96 | impl<T> ByAddress<T> |
97 | where |
98 | T: ?Sized + Deref, |
99 | { |
100 | /// Convenience method for pointer casts. |
101 | fn addr(&self) -> *const T::Target { |
102 | &*self.0 |
103 | } |
104 | } |
105 | |
106 | struct DebugAdapter<'a, T>(&'a T) |
107 | where |
108 | T: ?Sized + Deref + Debug; |
109 | |
110 | impl<'a, T> Debug for DebugAdapter<'a, T> |
111 | where |
112 | T: ?Sized + Deref + Debug, |
113 | { |
114 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
115 | self.0.fmt(f)?; |
116 | f.write_str(data:" @ " )?; |
117 | (self.0.deref() as *const T::Target).fmt(f)?; |
118 | Ok(()) |
119 | } |
120 | } |
121 | |
122 | impl<T> Debug for ByAddress<T> |
123 | where |
124 | T: ?Sized + Deref + Debug, |
125 | { |
126 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
127 | f&mut DebugTuple<'_, '_>.debug_tuple(name:"ByAddress" ) |
128 | .field(&DebugAdapter(&self.0)) |
129 | .finish() |
130 | } |
131 | } |
132 | |
133 | impl<T> Display for ByAddress<T> |
134 | where |
135 | T: ?Sized + Deref + Display, |
136 | { |
137 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
138 | self.0.fmt(f) |
139 | } |
140 | } |
141 | |
142 | /// Raw pointer equality |
143 | impl<T> PartialEq for ByAddress<T> |
144 | where |
145 | T: ?Sized + Deref, |
146 | { |
147 | fn eq(&self, other: &Self) -> bool { |
148 | self.addr() == other.addr() |
149 | } |
150 | } |
151 | impl<T> Eq for ByAddress<T> where T: ?Sized + Deref {} |
152 | |
153 | /// Raw pointer ordering |
154 | impl<T> Ord for ByAddress<T> |
155 | where |
156 | T: ?Sized + Deref, |
157 | { |
158 | fn cmp(&self, other: &Self) -> Ordering { |
159 | self.addr().cmp(&other.addr()) |
160 | } |
161 | } |
162 | |
163 | /// Raw pointer comparison |
164 | impl<T> PartialOrd for ByAddress<T> |
165 | where |
166 | T: ?Sized + Deref, |
167 | { |
168 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
169 | Some(self.addr().cmp(&other.addr())) |
170 | } |
171 | } |
172 | |
173 | /// Raw pointer hashing |
174 | impl<T> Hash for ByAddress<T> |
175 | where |
176 | T: ?Sized + Deref, |
177 | { |
178 | fn hash<H: Hasher>(&self, state: &mut H) { |
179 | self.addr().hash(state) |
180 | } |
181 | } |
182 | |
183 | // Generic conversion traits: |
184 | |
185 | impl<T> Deref for ByAddress<T> |
186 | where |
187 | T: ?Sized + Deref, |
188 | { |
189 | type Target = T; |
190 | |
191 | fn deref(&self) -> &Self::Target { |
192 | &self.0 |
193 | } |
194 | } |
195 | |
196 | impl<T> DerefMut for ByAddress<T> |
197 | where |
198 | T: ?Sized + Deref, |
199 | { |
200 | fn deref_mut(&mut self) -> &mut Self::Target { |
201 | &mut self.0 |
202 | } |
203 | } |
204 | |
205 | impl<T, U> AsRef<U> for ByAddress<T> |
206 | where |
207 | T: ?Sized + Deref + AsRef<U>, |
208 | { |
209 | fn as_ref(&self) -> &U { |
210 | self.0.as_ref() |
211 | } |
212 | } |
213 | |
214 | impl<T, U> AsMut<U> for ByAddress<T> |
215 | where |
216 | T: ?Sized + Deref + AsMut<U>, |
217 | { |
218 | fn as_mut(&mut self) -> &mut U { |
219 | self.0.as_mut() |
220 | } |
221 | } |
222 | |
223 | impl<T> From<T> for ByAddress<T> |
224 | where |
225 | T: Deref, |
226 | { |
227 | fn from(t: T) -> ByAddress<T> { |
228 | ByAddress(t) |
229 | } |
230 | } |
231 | |
232 | /// Similar to [`ByAddress`], but omits the attributes of fat pointers. |
233 | #[derive (Copy, Clone, Default)] |
234 | pub struct ByThinAddress<T>(pub T) |
235 | where |
236 | T: ?Sized + Deref; |
237 | |
238 | impl<T> ByThinAddress<T> |
239 | where |
240 | T: ?Sized + Deref, |
241 | { |
242 | /// Convenience method for pointer casts. |
243 | fn addr(&self) -> *const T::Target { |
244 | &*self.0 |
245 | } |
246 | } |
247 | |
248 | impl<T> Debug for ByThinAddress<T> |
249 | where |
250 | T: ?Sized + Deref + Debug, |
251 | { |
252 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
253 | f&mut DebugTuple<'_, '_>.debug_tuple(name:"ByThinAddress" ) |
254 | .field(&DebugAdapter(&self.0)) |
255 | .finish() |
256 | } |
257 | } |
258 | |
259 | impl<T> Display for ByThinAddress<T> |
260 | where |
261 | T: ?Sized + Deref + Display, |
262 | { |
263 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
264 | self.0.fmt(f) |
265 | } |
266 | } |
267 | |
268 | /// Raw pointer equality |
269 | impl<T> PartialEq for ByThinAddress<T> |
270 | where |
271 | T: ?Sized + Deref, |
272 | { |
273 | fn eq(&self, other: &Self) -> bool { |
274 | core::ptr::eq(self.addr() as *const (), b:other.addr() as *const _) |
275 | } |
276 | } |
277 | impl<T> Eq for ByThinAddress<T> where T: ?Sized + Deref {} |
278 | |
279 | /// Raw pointer ordering |
280 | impl<T> Ord for ByThinAddress<T> |
281 | where |
282 | T: ?Sized + Deref, |
283 | { |
284 | fn cmp(&self, other: &Self) -> Ordering { |
285 | (self.addr() as *const ()).cmp(&(other.addr() as *const ())) |
286 | } |
287 | } |
288 | |
289 | /// Raw pointer comparison |
290 | impl<T> PartialOrd for ByThinAddress<T> |
291 | where |
292 | T: ?Sized + Deref, |
293 | { |
294 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
295 | Some((self.addr() as *const ()).cmp(&(other.addr() as *const ()))) |
296 | } |
297 | } |
298 | |
299 | /// Raw pointer hashing |
300 | impl<T> Hash for ByThinAddress<T> |
301 | where |
302 | T: ?Sized + Deref, |
303 | { |
304 | fn hash<H: Hasher>(&self, state: &mut H) { |
305 | (self.addr() as *const ()).hash(state) |
306 | } |
307 | } |
308 | |
309 | // Generic conversion traits: |
310 | |
311 | impl<T> Deref for ByThinAddress<T> |
312 | where |
313 | T: ?Sized + Deref, |
314 | { |
315 | type Target = T; |
316 | |
317 | fn deref(&self) -> &Self::Target { |
318 | &self.0 |
319 | } |
320 | } |
321 | |
322 | impl<T> DerefMut for ByThinAddress<T> |
323 | where |
324 | T: ?Sized + Deref, |
325 | { |
326 | fn deref_mut(&mut self) -> &mut Self::Target { |
327 | &mut self.0 |
328 | } |
329 | } |
330 | |
331 | impl<T, U> AsRef<U> for ByThinAddress<T> |
332 | where |
333 | T: ?Sized + Deref + AsRef<U>, |
334 | { |
335 | fn as_ref(&self) -> &U { |
336 | self.0.as_ref() |
337 | } |
338 | } |
339 | |
340 | #[cfg (test)] |
341 | mod tests { |
342 | extern crate std; |
343 | use std::format; |
344 | |
345 | use crate::{ByAddress, ByThinAddress}; |
346 | |
347 | trait A: std::fmt::Debug { |
348 | fn test(&self) {} |
349 | } |
350 | trait B: A { |
351 | fn test2(&self) {} |
352 | } |
353 | |
354 | #[derive (Debug)] |
355 | struct Test {} |
356 | impl A for Test {} |
357 | impl B for Test {} |
358 | |
359 | fn force_vtable<O: B>(v: &O) -> &dyn A { |
360 | v |
361 | } |
362 | |
363 | #[test ] |
364 | fn test_thin_ptr_fail() { |
365 | let t = Test {}; |
366 | let tr1: &dyn A = &t; |
367 | let tr2: &dyn A = force_vtable(&t); |
368 | |
369 | let a = ByAddress(tr1); |
370 | let b = ByAddress(tr2); |
371 | |
372 | assert_ne!(a, b); |
373 | } |
374 | |
375 | #[test ] |
376 | fn test_thin_ptr_success() { |
377 | let t = Test {}; |
378 | let tr1: &dyn A = &t; |
379 | let tr2: &dyn A = force_vtable(&t); |
380 | |
381 | let a = ByThinAddress(tr1); |
382 | let b = ByThinAddress(tr2); |
383 | |
384 | assert_eq!(a, b); |
385 | } |
386 | |
387 | #[test ] |
388 | fn test_debug() { |
389 | let x = &1; |
390 | let b = ByAddress(x); |
391 | let expected = format!("ByAddress(1 @ {:p})" , x); |
392 | let actual = format!("{:?}" , b); |
393 | assert_eq!(expected, actual); |
394 | |
395 | let t = ByThinAddress(x); |
396 | let expected = format!("ByThinAddress(1 @ {:p})" , x); |
397 | let actual = format!("{:?}" , t); |
398 | assert_eq!(expected, actual); |
399 | } |
400 | } |
401 | |