kovan_map/hashmap/std_traits.rs
1//! The `std::collections::HashMap`-shaped trait set: `Default`, `Debug`, `Clone`, `Extend`,
2//! `PartialEq` and `Eq`, each with std's own bounds where an honest concurrent implementation can
3//! give them, built entirely on `HashMap`'s public API (`iter`, `get`, `len`,
4//! `with_capacity_and_hasher`, `insert`). None of it touches a write path's own body.
5//!
6//! `Clone` and `Extend<(K, V)>` also ask `K: MaybeSend, V: MaybeSend` (`Send` where threads share
7//! memory, see [`MaybeSend`]), as `FromIterator` does: an entry
8//! their inserts replace is retired, and kovan may run a retired entry's destructor on another
9//! thread (the contract of `kovan::retire`), which a `!Send` key or value must never meet.
10//! `Extend<(&K, &V)>` needs no `Send`: its `Copy` keys and values have no destructor.
11//!
12//! `FromIterator` and both `IntoIterator` impls live in `iter.rs`, next to the iterator types
13//! they return; `Send`/`Sync`/`Drop` stay in the main module, next to the field layout they
14//! reason about.
15
16use super::HashMap;
17use crate::MaybeSend;
18use core::fmt;
19use core::hash::{BuildHasher, Hash};
20
21/// Creates an empty map with `S`'s default hasher and the default capacity, bounded by exactly
22/// what `std::collections::HashMap`'s `Default` needs: `S: Default` (construction never hashes,
23/// so `K`/`V` carry only the struct's own `'static`, and `S` needs no `BuildHasher` either). Not
24/// `#[cfg(feature = "std")]`: unlike [`HashMap::new`] (which needs `foldhash::fast::FixedState`'s
25/// `std`-only default), this is generic over the caller's own `S`.
26impl<K: 'static, V: 'static, S: Default> Default for HashMap<K, V, S> {
27 fn default() -> Self {
28 Self::with_hasher(S::default())
29 }
30}
31
32/// A snapshot of the map's entries, as `std::collections::HashMap`'s `Debug` prints its own.
33/// Bounded by what the walk needs (owned clones), never by `Hash`, `Eq` or `S: BuildHasher`:
34/// printing hashes nothing.
35impl<K, V, S> fmt::Debug for HashMap<K, V, S>
36where
37 K: Clone + fmt::Debug + 'static,
38 V: Clone + fmt::Debug + 'static,
39{
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 f.debug_map().entries(self.iter()).finish()
42 }
43}
44
45/// A snapshot copy: a new map with the same capacity, the same shrink floor and a clone of the
46/// hasher, populated by inserting a clone of every entry this map held at some point during the
47/// walk. Like `iter`, a concurrent write to `self` during the clone may or may not be reflected
48/// in the result.
49impl<K, V, S> Clone for HashMap<K, V, S>
50where
51 K: Hash + Eq + Clone + MaybeSend + 'static,
52 V: Clone + MaybeSend + 'static,
53 S: BuildHasher + Clone,
54{
55 fn clone(&self) -> Self {
56 let mut cloned = Self::with_capacity_and_hasher(self.capacity(), self.hasher().clone());
57 // The source's floor, not its current size: sized for the entries it holds now, the
58 // clone still shrinks back as far as the source would once they are removed.
59 cloned.floor = self.floor;
60 for (k, v) in self.iter() {
61 cloned.insert(k, v);
62 }
63 cloned
64 }
65}
66
67/// Inserts every pair, through the same write path [`HashMap::extend`] (`&self`) uses: a `&mut
68/// self` receiver only because [`Extend`] requires one, never taken as exclusive access to the
69/// map's own writers.
70impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>
71where
72 K: Hash + Eq + Clone + MaybeSend + 'static,
73 V: Clone + MaybeSend + 'static,
74 S: BuildHasher,
75{
76 fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
77 HashMap::extend(&*self, iter);
78 }
79}
80
81/// As `std::collections::HashMap`'s `Extend<(&K, &V)>`: `K` and `V` copied out of the borrowed
82/// pairs, then inserted the same way as the owned-pair impl above.
83impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>
84where
85 K: Hash + Eq + Copy + MaybeSend + 'static,
86 V: Copy + MaybeSend + 'static,
87 S: BuildHasher,
88{
89 fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {
90 HashMap::extend(&*self, iter.into_iter().map(|(k, v)| (*k, *v)));
91 }
92}
93
94/// Content equality of a snapshot of each map: the same length and, for every pair this map's
95/// walk found, the other map holding that key with an equal value. Exact at quiescence and
96/// best-effort under a concurrent write to either map, the same honesty [`HashMap::len`]
97/// documents; a lock-free map cannot hold both maps still for the comparison the way an
98/// exclusively-borrowed `std::collections::HashMap` can.
99impl<K, V, S> PartialEq for HashMap<K, V, S>
100where
101 K: Hash + Eq + Clone + MaybeSend + 'static,
102 V: Clone + PartialEq + MaybeSend + 'static,
103 S: BuildHasher,
104{
105 fn eq(&self, other: &Self) -> bool {
106 self.len() == other.len()
107 && self
108 .iter()
109 .all(|(k, v)| other.get(&k).is_some_and(|ov| ov == v))
110 }
111}
112
113/// `V: Eq` makes the snapshot comparison above reflexive, symmetric and transitive at
114/// quiescence, as `std::collections::HashMap`'s `Eq` is.
115impl<K, V, S> Eq for HashMap<K, V, S>
116where
117 K: Hash + Eq + Clone + MaybeSend + 'static,
118 V: Clone + Eq + MaybeSend + 'static,
119 S: BuildHasher,
120{
121}