std.vector and std.map
The standard library ships two generic collections: std.vector, a growable array, and std.map, a hash map from string keys to values. Both are written in dusk itself; the sources are lib/std/vector.dusk and lib/std/map.dusk in the repository.
Both collections follow the same shape: the struct holds heap buffers that double when they fill, so you pass the collection by pointer so growth persists across calls. The usual pattern is to build one on the heap with alloc(...) and release it in two steps: the module’s *_free function frees the backing buffers, then free releases the struct itself. See /stdlib/memory/ for the allocation builtins these sit on.
std.vector
Section titled “std.vector”@import std.vectorA growable array, generic over its element type T. The buffer lives on the heap and doubles when it fills, so appends are amortized constant time. Element access is bounds checked, and the raw buffer never escapes the module.
export struct Vector<T> { data: *raw T, len: int64, cap: int64,}Functions
Section titled “Functions”| Function | Description |
|---|---|
vec_new<T>() -> Vector<T> | A new empty vector. |
vec_push<T>(v: *Vector<T>, x: T) -> void | Append one element, growing if needed. |
vec_get<T>(v: *Vector<T>, i: int64) -> T | The element at index i, bounds checked. |
vec_len<T>(v: *Vector<T>) -> int64 | The element count. |
vec_free<T>(v: *Vector<T>) -> void | Free the backing buffer. |
Capacity starts at 4 on the first push and doubles from there. vec_get checks that i is in 0..len; an out-of-range index prints fatal: vector index out of bounds and aborts the program.
Example
Section titled “Example”@paradigm procedural
@import std.vector
func main() -> int32 { v: *Vector<int64> = alloc(vec_new()) mut i: int64 = 0 while i < 5 { vec_push(v, i * 10) i = i + 1 } println(vec_len(v)) // 5 println(vec_get(v, 2)) // 20 vec_free(v) free(v) return 0}There is no dedicated iterator; loop over indexes 0 to vec_len(v) with vec_get, as above. The while loop and mut require the procedural paradigm. See /reference/paradigm-system/.
std.map
Section titled “std.map”@import std.mapA hash map from string keys to values, generic over the value type V. It uses open addressing with linear probing over heap buffers that double and rehash once the table is half full, which keeps an empty slot reachable so a probe always terminates. Keys are compared by their NUL-terminated bytes.
export struct Map<V> { used: *raw bool, keys: *raw string, vals: *raw V, len: int64, cap: int64,}Functions
Section titled “Functions”| Function | Description |
|---|---|
map_new<V>() -> Map<V> | A new empty map. |
map_put<V>(m: *Map<V>, k: string, v: V) -> void | Insert the value, or overwrite the key. |
map_get<V>(m: *Map<V>, k: string) -> Maybe<V> | The value for a key, or None when absent. |
map_has<V>(m: *Map<V>, k: string) -> bool | True when the key is present. |
map_len<V>(m: *Map<V>) -> int64 | The entry count. |
map_free<V>(m: *Map<V>) -> void | Free the backing buffers. |
map_hash(s: string) -> int64 | The key hash, exposed for reuse. |
Capacity starts at 8 and doubles each time the map fills to half. map_put on an existing key overwrites the value and does not change map_len. map_free releases the map’s buffers but not the key strings, which the caller still owns.
map_hash is a polynomial rolling hash over the key bytes. Overflow wraps, so the result may be negative; the map folds a negative index back into range internally.
The module has no function that iterates over its entries.
Example
Section titled “Example”map_get returns a Maybe<V> from /stdlib/functional/, so import std.functional.maybe to work with the result.
@paradigm procedural
@import std.map@import std.functional.maybe
func main() -> int32 { m: *Map<int64> = alloc(map_new()) map_put(m, "one", 1) map_put(m, "two", 2) map_put(m, "two", 22) // overwrites, len stays 2 println(map_len(m)) // 2 println(unwrap_or(map_get(m, "two"), 0)) // 22 println(unwrap_or(map_get(m, "ten"), 0)) // 0 if map_has(m, "one") { println("one is present") } map_free(m) free(m) return 0}Instead of unwrap_or, you can match on the result directly.
@paradigm procedural
@import std.map@import std.functional.maybe
func main() -> int32 { m: *Map<string> = alloc(map_new()) map_put(m, "lang", "dusk") match map_get(m, "lang") { Some(v) => println(v), None => println("missing"), } map_free(m) free(m) return 0}Ownership summary
Section titled “Ownership summary”- Both collections own their heap buffers.
vec_freeandmap_freerelease the buffers only. - A collection built on the heap with
alloc(...)needs a secondfreefor the struct itself, as in the examples above. - The map does not copy or free its key strings; the caller keeps ownership of them, and they must outlive the map.
For the rest of the standard library, see /stdlib/overview/.