context_trait/
hash_ctx.rs1use crate::WithContext;
4use std::hash::{Hash, Hasher};
5
6pub struct HashContext<T> {
37 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}