Skip to content
CryoCryo home
Stdlibcollections

hashmap

import std::collections::hashmap; · source

Types

HashMap<K, V, A>

type struct HashMap<K, V, A = GlobalAlloc> where A: Allocator {
    buckets:      Entry<K, V>**;
    bucket_count: u64;
    length:       u64;
    alloc:        A;
    seed:         u64;

    static new() -> HashMap<K, V, GlobalAlloc>;
    static new_in(alloc: A) -> HashMap<K, V, A>;
    static with_seed(seed: u64) -> HashMap<K, V, GlobalAlloc>;
    static with_seed_in(seed: u64, alloc: A) -> HashMap<K, V, A>;
    static with_capacity(capacity: u64) -> HashMap<K, V, GlobalAlloc>;
    length(&this) -> u64;
    is_empty(&this) -> boolean;
    bucket_count(&this) -> u64;
    get_bucket(&this, index: u64) -> Entry<K, V>*;

    iter(&this) -> implement Iterator<Pair<K, V>>
    where K: Copy, V: Copy;

    keys(&this) -> implement Iterator<K>
    where K: Copy;

    values(&this) -> implement Iterator<V>
    where V: Copy;

    entries(&this) -> implement Iterator<Pair<K*, V*>>;
    keys_ref(&this) -> implement Iterator<K*>;
    values_ref(&this) -> implement Iterator<V*>;

    contains_key(&this, key: &K) -> boolean
    where K: Hash + Eq;

    get(&this, key: &K) -> Option<V>
    where K: Hash + Eq, V: Copy;

    get_ref(&this, key: &K) -> Option<V*>
    where K: Hash + Eq;

    insert(mut &this, key: K, value: V) -> Option<V>
    where K: Hash + Eq;

    try_insert(mut &this, key: K, value: V) -> Result<Option<V>, AllocError>
    where K: Hash + Eq;

    get_or_insert(mut &this, key: K, default: V) -> V*
    where K: Hash + Eq;

    try_get_or_insert(mut &this, key: K, default: V) -> Result<V*, AllocError>
    where K: Hash + Eq;

    remove(mut &this, key: &K) -> Option<V>
    where K: Hash + Eq;

    clear(mut &this) -> void;
    resize_buckets(mut &this, new_count: u64) -> Result<(), AllocError>;
}

Separate chaining over a bucket array: each bucket holds a linked list of Entry nodes. Keys must implement Hash + Eq; the bound sits on each method rather than the type, so a map of un-hashable keys is spellable but unusable.

import std::collections::hashmap;

mut counts: HashMap<Str, u64> = HashMap::<Str, u64>::new();
mut slot: u64* = counts.get_or_insert(word, 0);
*slot = *slot + 1;

get reads by value and needs V: Copy; get_ref and get_or_insert hand back a V* and are sound for owning values. insert returns the value it replaced, and remove drops the key and returns the value. bucket_count is buckets allocated, not capacity — chaining means one bucket holds many entries — and get_bucket is the head of a chain for manual traversal. try_insert and try_get_or_insert are the fallible forms.

Properties

PropertyTypeNotes
bucketsEntry<K, V>**
bucket_countu64
lengthu64
allocA
seedu64Per-map hash seed (initial DefaultHasher state). Drawn from the process seed generator (next_map_seed) so bucket placement is unpredictable between runs; fixed via with_seed* for tests that need reproducible iteration/layout.

Constructors

static new() -> HashMap<K, V, GlobalAlloc>;
static new_in(alloc: A) -> HashMap<K, V, A>;
static with_seed(seed: u64) -> HashMap<K, V, GlobalAlloc>;

Construct an empty map with an explicit, fixed hash seed instead of a random one. Intended for tests and any caller that needs reproducible bucket placement / iteration order; production code should prefer new so the seed stays unpredictable.

static with_seed_in(seed: u64, alloc: A) -> HashMap<K, V, A>;

with_seed in a caller-supplied allocator.

static with_capacity(capacity: u64) -> HashMap<K, V, GlobalAlloc>;

Preallocate for at least capacity entries at the default load factor. The actual bucket count is the smallest power of two that keeps the load factor below 1.0.

Methods

length(&this) -> u64;
is_empty(&this) -> boolean;
bucket_count(&this) -> u64;

Number of buckets currently allocated. Not the same as the number of entries the map can hold; chaining means every bucket can hold many entries.

get_bucket(&this, index: u64) -> Entry<K, V>*;

Return the head of the entry chain for bucket index. Returns null if the bucket is empty or index is out of range. The returned pointer is valid until the next mutation of the map.

iter(&this) -> implement Iterator<Pair<K, V>> where K: Copy, V: Copy;

Iterate over every entry, yielding Pair<K, V> (key first, value second). Iteration order is bucket-major and undefined across resizes; do not rely on it.

keys(&this) -> implement Iterator<K> where K: Copy;

Iterate over the keys, yielding each by value (bitwise copy). Gated on K: Copy for the same reason as iter. Order is bucket-major and unstable across resizes.

values(&this) -> implement Iterator<V> where V: Copy;

Iterate over the values, yielding each by value (bitwise copy). Gated on V: Copy. Order is bucket-major and unstable.

entries(&this) -> implement Iterator<Pair<K*, V*>>;

Iterate over every entry, yielding Pair<K*, V*> (borrowed key and value). Unlike iter, this is sound for any K/V (owning or Copy) because it hands out borrows, never bitcopies - so it is the way to walk a HashMap<String, Foo> without cloning. The pointers borrow map-owned storage and are invalidated by any mutation that inserts, removes, or resizes; same contract as get_ref. Order is bucket-major and unstable across resizes.

keys_ref(&this) -> implement Iterator<K*>;

Iterate over the keys by shared borrow, yielding K*. Sound for any K (owning or Copy); this is how to walk the keys of a HashMap<String, V> - the common case - without the K: Copy gate on keys. Same borrow/invalidation contract as entries.

values_ref(&this) -> implement Iterator<V*>;

Iterate over the values by shared borrow, yielding V*. Sound for any V (owning or Copy). Same borrow/invalidation contract as entries; use this to read or mutate owning values in place without the V: Copy gate on values.

contains_key(&this, key: &K) -> boolean where K: Hash + Eq;
get(&this, key: &K) -> Option<V> where K: Hash + Eq, V: Copy;

Look up a value by key, returning a bitwise copy. Gated on V: Copy for the same reason as Array::get: handing back a duplicate of an owning value would alias storage the map still owns, double-freeing on drop. For owning V, borrow in place with get_ref (or move it out with remove).

get_ref(&this, key: &K) -> Option<V*> where K: Hash + Eq;

Like get, but returns a borrow (raw pointer) into the entry's stored value rather than a copy. Use when the value owns heap resources you don't want to clone, or when callers need to mutate the stored value in place.

insert(mut &this, key: K, value: V) -> Option<V> where K: Hash + Eq;

Insert key -> value. Returns the previous value if the key was present, otherwise None. Panics on allocation failure; use try_insert to recover from OOM.

try_insert(mut &this, key: K, value: V) -> Result<Option<V>, AllocError> where K: Hash + Eq;
get_or_insert(mut &this, key: K, default: V) -> V* where K: Hash + Eq;

Borrow the value for key, inserting default first if the key is absent. Returns a pointer to the (existing or freshly inserted) value, valid until the next mutation that removes this key or drops the map (a resize keeps entry storage in place). A single lookup, unlike get_ref-then-insert. Panics on allocation failure; the common case (key present) never allocates.

try_get_or_insert(mut &this, key: K, default: V) -> Result<V*, AllocError> where K: Hash + Eq;

Fallible get_or_insert: returns Err(AllocError) instead of panicking when a needed resize or entry allocation fails.

remove(mut &this, key: &K) -> Option<V> where K: Hash + Eq;

Remove the entry with matching key. Returns the removed value, or None if the key wasn't present.

clear(mut &this) -> void;

Drop every entry. Retains the bucket array so subsequent inserts don't re-pay the allocation.

resize_buckets(mut &this, new_count: u64) -> Result<(), AllocError>;

Entry<K, V>

type struct Entry<K, V> {
    hash:  u64;
    key:   K;
    value: V;
    next:  Entry<K, V>*;
}

One node of a bucket's chain. get_bucket hands back the head of a chain as an Entry*, and next walks it; the cached hash is what lets a resize redistribute entries without re-hashing the keys. You only touch it for manual traversal.

Properties
PropertyType
hashu64
keyK
valueV
nextEntry<K, V>*

Iterating

iter(), keys(), and values() yield by value and carry Copy bounds. entries(), keys_ref(), and values_ref() yield Pair<K*, V*>, K*, and V* respectively, and are sound for any key and value type — so they are what you want for a HashMap<String, Foo>. Order is bucket-major and unstable across resizes; every borrow is invalidated by any insert, remove, or resize.

Hash seeding

The hasher is FNV-1a, seeded per map from a per-process random value drawn from the kernel CSPRNG. Bucket placement therefore varies between runs, so adversarial keys cannot be precomputed to flood a single bucket. FNV-1a is not collision-resistant, so treat this as hardening rather than a cryptographic guarantee — supply a custom hasher where it matters.

with_seed(seed) and with_seed_in(seed, alloc) fix the seed for tests that need reproducible layout or iteration order. Production code should use new.

Chaining was chosen over open addressing because it sidesteps storing uninitialized key/value slots. The cost is one heap allocation per entry.

Trait implementations

implement<K, V, A> trait Drop for struct HashMap<K, V, A> where A: Allocator
implement<K, V> trait Clone for struct HashMap<K, V, GlobalAlloc> where K: Hash + Eq + Clone, V: Clone
implement<K, V> trait Default for struct HashMap<K, V, GlobalAlloc>
implement<K, V, A> trait Display for struct HashMap<K, V, A> where K: Display, V: Display, A: Allocator
std::fmt::display
implement<K, V, A> trait Debug for struct HashMap<K, V, A> where K: Debug, V: Debug, A: Allocator
std::fmt::display

Clone re-inserts every entry into a fresh map, so the clone's bucket layout may differ. Default is a fresh empty map that allocates no buckets until the first insert. Display renders {k: v, ...}.

Constants

const INITIAL_BUCKETS: u64 = 8;
const SEED_GAMMA:      u64 = 0x9E3779B97F4A7C15;
const SEED_MIX_1:      u64 = 0xBF58476D1CE4E5B9;
const SEED_MIX_2:      u64 = 0x94D049BB133111EB;