nohash

A pass-through Rust Hasher that turns integer HashMap and HashSet lookups into a single stored value instead of a hash computation.

Library
Cargo
v0.2.0
9stars
Apache-2.0 OR MIT

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
19/100Needs Attention
Development Activity0
Maintenance0
Community16
Maturity60
Momentum0

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
66/100Good
Architecture70
Code Quality65
Innovation72
Learning Curve55

nohash is a minimal Rust crate implementing std::hash::Hasher for keys that are already integers, skipping the actual hashing step entirely. Instead of running an integer through a general-purpose algorithm like SipHash, NoHashHasher<T> simply stores the value passed to one write_* call and returns it unchanged from finish(), turning HashMap/HashSet lookups keyed by small integers into near no-op memory operations.

The crate ships IntMap<K, V> and IntSet<T> type aliases that wrap std::collections::HashMap/HashSet with BuildNoHashHasher pre-wired, plus the IsEnabled marker trait so custom types (like newtype wrappers around integer IDs) can opt in as long as their Hash implementation writes exactly one primitive value. In debug builds, the hasher asserts that assumption is upheld, panicking if write_* is called more than once — providing a safety net during development without any runtime cost in release builds. The crate is no_std-compatible for embedded and constrained environments, gated behind the default std feature flag.

What You Get

  • NoHashHasher<T> - a Hasher implementation that stores the raw integer passed to write_u8/write_u32/etc. and returns it unchanged from finish()
  • IntMap<K, V> and IntSet<T> aliases - HashMap/HashSet type aliases pre-configured with BuildNoHashHasher, ready to use as drop-in replacements
  • IsEnabled marker trait - lets custom integer-backed types (IDs, newtypes) opt into NoHashHasher once their Hash impl is verified to write exactly one value
  • Debug-mode misuse assertions - panics in debug builds if a hasher instance receives more than one write_* call, catching incorrect Hash implementations early
  • no_std support - builds without the standard library when the default std feature is disabled, for embedded and constrained targets

Common Use Cases

  • Hot-path integer-keyed maps - a game engine or simulation storing per-entity state in a HashMap<EntityId, Component> skips SipHash entirely on every lookup
  • Interned/ID-based caches - compilers and interpreters that already use dense integer IDs for symbols or nodes avoid re-hashing IDs that are already well distributed
  • Custom newtype keys - a project can wrap a u64 in a newtype, implement IsEnabled, and get the same no-hash speedup without exposing raw integers throughout the codebase
  • no_std embedded systems - firmware or kernel code needing an integer-keyed hash map without pulling in a full general-purpose hashing algorithm

Under The Hood

Architecture nohash is a single flat module (src/lib.rs, no submodules) that defines one generic struct, NoHashHasher<T>, in two mutually exclusive forms selected by #[cfg(debug_assertions)]: a debug build carries an extra bool flag used to assert that write_* is invoked exactly once, while a release build drops that field entirely for zero runtime overhead. Two Hasher trait implementations mirror this split, and a PhantomData<T> marker ties each hasher instance to the key type it’s meant to be used with without storing any actual T value. The public surface is completed by the IsEnabled marker trait, which types opt into to signal their Hash implementation writes exactly one primitive, and the IntMap<K, V>/IntSet<T> type aliases, which wrap std::collections::HashMap/HashSet with BuildHasherDefault<NoHashHasher<K>> so callers never construct the hasher directly. Because nothing downstream depends on NoHashHasher’s internal representation — only its Hasher/Default/Clone/Copy trait implementations are consumed — changing the core struct’s storage would only ripple into the two impl blocks and the type aliases in the same file.

Tech Stack The crate has zero external dependencies — Cargo.toml declares no [dependencies] entries at all, relying purely on core::hash::{BuildHasherDefault, Hasher} and, behind the default std feature flag, std::collections::{HashMap, HashSet}. Disabling the std feature compiles the crate under #![cfg_attr(not(feature = “std”), no_std)], dropping the IntMap/IntSet aliases but keeping NoHashHasher and IsEnabled usable in embedded or kernel contexts. It targets the 2018 edition, is dual-licensed Apache-2.0 OR MIT, and ships no build scripts, macros, or proc-macro dependencies. CI (.github/workflows/rust.yml) runs cargo build and cargo test on GitHub Actions for pushes and PRs against master, with no separate lint or formatting job.

Code Quality Tests live inline in a #[cfg(test)] mod tests block at the bottom of lib.rs: a single ok test exercises all ten supported primitive types’ write_* → finish() round trip, and ten separate #[should_panic] tests (one per type, gated on debug_assertions) verify the double-write guard fires correctly. Every public item also carries a doc comment with a runnable example, which cargo test executes as doctests, giving the crate meaningful coverage of its narrow surface despite having no dedicated tests/ integration directory. There’s no clippy or rustfmt configuration checked in, and CI doesn’t run either, so style and lint enforcement rely on contributor discipline rather than automation. Error handling is minimal by design — the write(&mut self, _: &[u8]) fallback simply panics, encoding the crate’s core invariant (exactly one primitive write per hash) as a hard failure rather than a Result, which is an intentional and idiomatic choice for a Hasher implementation.

API Design The public API is deliberately tiny: one generic struct, one marker trait, and two pre-wired collection aliases, so adopting it is typically a one-line swap from HashMap<K, V> to IntMap<K, V> with IntMap::default() replacing HashMap::new(). Naming follows std::hash conventions closely (BuildNoHashHasher mirrors BuildHasherDefault, IsEnabled reads like a capability marker trait), and every public item’s doc comment includes a compiling usage example, so there’s no separate guide or example directory to hunt through. The debug-mode assertion is a notable DX touch — it turns a hard-to-diagnose logic bug (a Hash impl that writes more than one primitive) into an immediate panic during development while costing nothing in release builds. The design isn’t novel — trivial/no-op hashers for pre-hashed or already-integer keys are a known pattern in the Rust ecosystem, alongside crates like rustc-hash and fxhash (though those hash rather than pass through) — but the execution here is clean, minimal, and well-documented for its narrow purpose.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search