pub type NoHash = BuildHasherDefault<NoHasher>;
Expand description
§No-Hash (Passthrough) Hash State.
Hashing can be expensive, and is totally unnecessary for most numeric or
pre-hashed types. (You don’t need a hash to tell you that 1_u8
is
different than 2_u8
!)
NoHash
is a drop in replacement for the standard library’s hasher used in
HashMap
and HashSet
that lets
the values speak for themselves (e.g. hash(13_u16) == 13_u64
), bringing a
free performance boost.
This idea isn’t new, but unlike the hashers offered by nohash
or prehash
,
NoHash
does not limit itself to primitives or require any custom trait
implementations.
It “just works” for any type whose std::hash::Hash
implementation writes
a single <= 64-bit integer via one of the following:
write_i8
write_i16
write_i32
write_i64
write_isize
(if the target pointer width is <= 64)write_u8
write_u16
write_u32
write_u64
write_usize
(if the target pointer width is <= 64)
In other words, NoHash
can always be used for i8
, i16
, i32
, i64
,
u8
, u16
, u32
, u64
, all their NonZero
and Wrapping
counterparts,
and any custom types that derive their hashes from one of these types.
(isize
and usize
will work on most platforms too, just not those with
monstrous 128-bit pointer widths.)
§Examples
use dactyl::NoHash;
use std::collections::{HashMap, HashSet};
let mut set: HashSet<u32, NoHash> = HashSet::default();
assert!(set.insert(0_u32));
assert!(set.insert(1_u32));
assert!(set.insert(2_u32));
assert!(! set.insert(2_u32)); // Not unique!
let mut set: HashMap<i8, &str, NoHash> = HashMap::default();
assert_eq!(set.insert(-2_i8, "Hello"), None);
assert_eq!(set.insert(-1_i8, "World"), None);
assert_eq!(set.insert(0_i8, "How"), None);
assert_eq!(set.insert(1_i8, "Are"), None);
assert_eq!(set.insert(1_i8, "You?"), Some("Are")); // Not unique!
This can also be used with custom types that implement Hash
in such a
way that only a single specialized write_*
call occurs.
use dactyl::NoHash;
use std::{
collections::HashSet,
hash::{Hash, Hasher},
};
struct Person {
name: String,
id: u64,
}
impl Eq for Person {}
impl Hash for Person {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.id);
// Note: `self.id.hash(state)` would also work because it just
// calls `write_u64` under-the-hood.
}
}
impl PartialEq for Person {
fn eq(&self, b: &Self) -> bool { self.id == b.id }
}
let mut set: HashSet<Person, NoHash> = HashSet::default();
assert!(set.insert(Person { name: "Jane".to_owned(), id: 5 }));
assert!(set.insert(Person { name: "Joan".to_owned(), id: 6 }));
assert!(! set.insert(Person { name: "Jack".to_owned(), id: 6 })); // Duplicate ID.
§Panics
NoHash
does not support slices, i128
, or u128
as they cannot be
losslessly converted to u64
. If a Hash
implementation tries to make use
of those write methods, it will panic. On 128-bit platforms, attempts to hash
isize
or usize
will likewise result in a panic.
NoHash
will also panic if a Hash
implementation writes two or more
values to the hasher — as a tuple would, for example — but only for debug
builds. When building in release mode, NoHash
will simply pass-through
the last integer written to it, ignoring everything else.
Aliased Type§
struct NoHash(/* private fields */);