Skip to main content

context_trait/
hash_ctx.rs

1//! [`HashContext`]: custom hashing via function pointer.
2
3use crate::WithContext;
4use std::hash::{Hash, Hasher};
5
6/// A context providing a custom hash function.
7///
8/// When used with [`WithContext`], implements `Hash` by dispatching
9/// through the stored hash function. The function receives a `&mut dyn Hasher`
10/// so it can work with any hasher type.
11///
12/// # Examples
13///
14/// ```
15/// use context_trait::{WithContext, HashContext};
16/// use std::collections::hash_map::DefaultHasher;
17/// use std::hash::{Hash, Hasher};
18///
19/// let ctx = HashContext { hash: |v: &(i32, i32), h: &mut dyn Hasher| {
20///     h.write_i32(v.0);
21/// }};
22/// let a = WithContext { inner: (1, 100), ctx };
23///
24/// let mut hasher = DefaultHasher::new();
25/// a.hash(&mut hasher);
26/// let hash_a = hasher.finish();
27///
28/// let b = WithContext { inner: (1, 200), ctx };
29/// let mut hasher = DefaultHasher::new();
30/// b.hash(&mut hasher);
31/// let hash_b = hasher.finish();
32///
33/// // Same hash because only first element is hashed
34/// assert_eq!(hash_a, hash_b);
35/// ```
36pub struct HashContext<T> {
37    /// The hash function.
38    pub hash: fn(&T, &mut dyn Hasher),
39}
40
41impl<T> Clone for HashContext<T> {
42    fn clone(&self) -> Self {
43        *self
44    }
45}
46impl<T> Copy for HashContext<T> {}
47impl<T> std::fmt::Debug for HashContext<T> {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        f.debug_struct("HashContext")
50            .field("hash", &(self.hash as usize))
51            .finish()
52    }
53}
54
55impl<T> Hash for WithContext<T, HashContext<T>> {
56    fn hash<H: Hasher>(&self, state: &mut H) {
57        (self.ctx.hash)(&self.inner, state);
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use std::collections::hash_map::DefaultHasher;
65
66    #[test]
67    fn custom_hash_ignores_second_field() {
68        let ctx = HashContext {
69            hash: |v: &(i32, i32), h: &mut dyn Hasher| {
70                h.write_i32(v.0);
71            },
72        };
73
74        let a = WithContext {
75            inner: (1, 100),
76            ctx,
77        };
78        let b = WithContext {
79            inner: (1, 999),
80            ctx,
81        };
82
83        let hash_of = |w: &WithContext<(i32, i32), HashContext<(i32, i32)>>| {
84            let mut h = DefaultHasher::new();
85            w.hash(&mut h);
86            h.finish()
87        };
88
89        assert_eq!(hash_of(&a), hash_of(&b));
90    }
91
92    #[test]
93    fn different_values_different_hash() {
94        let ctx = HashContext {
95            hash: |v: &i32, h: &mut dyn Hasher| {
96                h.write_i32(*v);
97            },
98        };
99
100        let a = WithContext { inner: 1, ctx };
101        let b = WithContext { inner: 2, ctx };
102
103        let hash_of = |w: &WithContext<i32, HashContext<i32>>| {
104            let mut h = DefaultHasher::new();
105            w.hash(&mut h);
106            h.finish()
107        };
108
109        assert_ne!(hash_of(&a), hash_of(&b));
110    }
111}