kumo_server_common/
hashable_weak.rs

1use std::hash::{Hash, Hasher};
2use std::sync::{Arc, Weak};
3
4/// `Weak<T>` newtype that hashes and compares by the underlying
5/// allocation address, so it can be stored in a `HashSet` or used
6/// as a `HashMap` key.
7///
8/// Equality uses `Weak::ptr_eq`. The `ptr_eq` semantics are
9/// documented as unspecified once the original allocation has been
10/// dropped, so callers should periodically prune dead entries (via
11/// `Weak::upgrade` returning `None`) to keep the set hygienic. With
12/// timely pruning, the only observable consequence of a stale
13/// entry colliding with a new live entry is that the live entry
14/// replaces the stale one on insert — which is the desired outcome
15/// anyway.
16pub struct HashableWeak<T: ?Sized>(pub Weak<T>);
17
18impl<T: ?Sized> HashableWeak<T> {
19    pub fn new(arc: &Arc<T>) -> Self {
20        Self(Arc::downgrade(arc))
21    }
22}
23
24impl<T> Hash for HashableWeak<T> {
25    fn hash<H: Hasher>(&self, state: &mut H) {
26        (self.0.as_ptr() as usize).hash(state)
27    }
28}
29
30impl<T: ?Sized> PartialEq for HashableWeak<T> {
31    fn eq(&self, other: &Self) -> bool {
32        Weak::ptr_eq(&self.0, &other.0)
33    }
34}
35
36impl<T: ?Sized> Eq for HashableWeak<T> {}
37
38impl<T: ?Sized> Clone for HashableWeak<T> {
39    fn clone(&self) -> Self {
40        Self(self.0.clone())
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use std::collections::HashSet;
48
49    #[test]
50    fn dedup_by_pointer_identity() {
51        let a = Arc::new(42u32);
52        let b = Arc::new(42u32);
53        let mut set: HashSet<HashableWeak<u32>> = HashSet::new();
54        set.insert(HashableWeak::new(&a));
55        set.insert(HashableWeak::new(&a));
56        set.insert(HashableWeak::new(&b));
57        assert_eq!(set.len(), 2);
58    }
59
60    #[test]
61    fn prune_via_upgrade() {
62        let a = Arc::new(42u32);
63        let mut set: HashSet<HashableWeak<u32>> = HashSet::new();
64        set.insert(HashableWeak::new(&a));
65        drop(a);
66        set.retain(|hw| hw.0.upgrade().is_some());
67        assert_eq!(set.len(), 0);
68    }
69
70    #[test]
71    fn iter_upgrade_collects_live() {
72        let a = Arc::new(1u32);
73        let b = Arc::new(2u32);
74        let mut set: HashSet<HashableWeak<u32>> = HashSet::new();
75        set.insert(HashableWeak::new(&a));
76        set.insert(HashableWeak::new(&b));
77        drop(a);
78        let live: Vec<u32> = set
79            .iter()
80            .filter_map(|hw| hw.0.upgrade().map(|v| *v))
81            .collect();
82        assert_eq!(live, vec![2]);
83    }
84}