silk/hash_map
Owned key-value storage with deterministic seeded hashing and open-addressed lookup.
When to use
Use HashMap for key-based lookup when a key has a HashKey witness. Use
silk.vector.Vector when presentation order should follow insertion, or when indexing rather
than equivalence is the primary operation.
Details
The map starts allocation-free, first allocates eight buckets, and grows before used buckets exceed three quarters of the table. Linear probing crosses removal markers; growth doubles the table, moves every live entry, and discards those markers. Allocation completes before the map commits, so failed growth leaves existing entries, length, and bucket count unchanged.
One HashSeed plus the same operation sequence fixes bucket presentation order across the
evaluator, native code, and WebAssembly. Iterate deterministically by scanning
0..bucketCount, testing occupiedAt, then reading keyAt and valueAt. This is bucket
order, not insertion order.
Gotchas
Lookup and removal consume the probe key. get, keyAt, and valueAt copy a complete
stored entry and therefore require both stored key and value to be Copy; use withMut to
update a move-only value in place or remove to transfer it out. Equivalent keys must also
obey the HashKey hash contract.
Examples
Insert, read, and remove one value
import silk.allocator { Allocator }
import silk.effect { Effect }
import silk.hash as Hash
import silk.hash_map as HashMap
import silk.option as Option
effect fn build() -> i32
! Allocator.OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let mut map = HashMap.make<Hash.Word, i32>(Hash.seed(17))
let inserting = HashMap.insert<Hash.Word, i32>(&mut map, Hash.word(7), 42)
|> Effect.provideMut<Allocator>(&mut allocator)
let previous = run inserting
drop previous
let found = HashMap.get<Hash.Word, i32>(&map, Hash.word(7))
|> Option.unwrapOr<i32>(0)
let removed = HashMap.remove<Hash.Word, i32>(&mut map, Hash.word(7))
drop removed
if HashMap.contains<Hash.Word, i32>(&map, Hash.word(7)) {
return 0
}
return found
}
effect fn recover(error: Allocator.OutOfMemoryError) -> i32 {
return 0
}
pub fn main() -> i32 {
return run Effect.catchAll(build(), recover)
}Import as HashMap with import silk.hash_map.
Public declarations: 16.
Entry
pub struct Entry<K, V>Internal key-value record exposed by the current table representation.
Implementation Copy for silk/hash_map.Entry<K, V>
impl Copy for silk/hash_map.Entry<K, V>Unallocated
pub struct Unallocated<K, V>Allocation-free storage state used before the map creates its first table.
Table
pub struct Table<K, V>Allocated entry and occupancy buffers used by HashMap.
HashMap
pub struct HashMap<K, V>Owns unique keys and their values under one equivalence, hash witness, and seed.
Details
An equivalent insertion replaces the stored value instead of adding another entry. The seed and operation sequence determine bucket presentation order, which is not insertion order.
make
pub fn make<K, V>(seed: HashSeed) -> silk/hash_map.HashMap<K, V>Constructs an empty map whose every hash is computed under one seed.
Details
An empty map allocates nothing. The seed fixes the order the map will present its entries in, and is the only thing besides the sequence of operations that decides it.
length
pub fn length<K, V>(self: &silk/hash_map.HashMap<K, V>) -> usizeReturns the number of entries the map holds.
bucketCount
pub fn bucketCount<K, V>(self: &silk/hash_map.HashMap<K, V>) -> usizeReturns the number of buckets the map presents, which is the range occupiedAt accepts.
occupiedAt
pub fn occupiedAt<K, V>(self: &silk/hash_map.HashMap<K, V>, index: usize) -> boolReports whether one bucket holds an entry. Out-of-range buckets hold nothing.
Implementation Drop for silk/hash_map.HashMap<K, V>
impl Drop for silk/hash_map.HashMap<K, V>insert
pub effect fn insert<K, V>(self: &mut silk/hash_map.HashMap<K, V>, key: K, value: V) -> Option<V> ! OutOfMemoryError ? &mut AllocatorInserts one owned key and value, answering with the value an equivalent key already held.
Details
The map takes ownership of both. When an equivalent key is already present the map's length does not change, the replaced value travels to the caller, and the key the map held is released.
Fails only with OutOfMemoryError, and only from the growth this insert needed. A failed insert
leaves every prior entry at its own key, and leaves the length and the bucket count unchanged.
contains
pub fn contains<K, V>(self: &silk/hash_map.HashMap<K, V>, key: K) -> boolReports whether the map holds an entry under a key equivalent to one probe key.
Details
This function consumes the probe key. It does not change the map or move a stored entry.
indexOf
pub fn indexOf<K, V>(self: &silk/hash_map.HashMap<K, V>, key: K) -> Option<usize>Returns the bucket holding an entry under a key equivalent to one probe key, or an absent value.
Details
This is the lookup a map with move-only values answers: the bucket names the entry without
moving anything out of the map. A move-only value can then be transferred with remove.
This function consumes the probe key.
get
pub fn get<K, V>(self: &silk/hash_map.HashMap<K, V>, key: K) -> Option<V>Returns the value held under a key equivalent to one probe key, or an absent value.
Details
Reads a complete entry copy, so it answers only when both stored key and value types are Copy.
Use indexOf for a non-moving presence check and remove to transfer a move-only value.
This function consumes the probe key and does not change the map.
withMut
pub fn withMut<K, V, F>(self: &mut silk/hash_map.HashMap<K, V>, key: K, use: F) -> boolRuns one take-once callback with exclusive access to an existing value.
Details
Lookup and mutation allocate nothing and never grow the map. Returns true after running the
callback exactly once for an equivalent key, or false without running it when the key is
absent. The unit callback cannot return its value borrow, and a callback that may park is
rejected.
This function consumes the probe key but leaves the stored key, length, used count, and bucket count unchanged.
remove
pub fn remove<K, V>(self: &mut silk/hash_map.HashMap<K, V>, key: K) -> Option<V>Removes the entry under a key equivalent to one probe key and answers with its value.
Details
Ownership of the value passes to the caller; the map does not also release it. The key the map held is released, and the probe key is released as well.
keyAt
pub fn keyAt<K, V>(self: &silk/hash_map.HashMap<K, V>, index: usize) -> KReturns the key held in one bucket. Traps on a bucket that holds no entry.
Details
Reads a complete entry copy, so both stored key and value types must be Copy.
Gotchas
If index is out of range or occupiedAt returns false, the program traps.
valueAt
pub fn valueAt<K, V>(self: &silk/hash_map.HashMap<K, V>, index: usize) -> VReturns the value held in one bucket. Traps on a bucket that holds no entry.
Details
Reads a complete entry copy, so both stored key and value types must be Copy.
Gotchas
If index is out of range or occupiedAt returns false, the program traps.