A map {K: V} stores values by key and remembers the order keys were first
inserted. Reading a key that isn’t there panics, so test with in or use
m.get(k), which returns an Option. Like arrays, maps are values: let b = a
copies.
fn main(): var ages = {"ann": 31, "bob": 27} ages["cy"] = 40 ages["ann"] += 1 print(ages, len(ages)) if "bob" in ages: print(ages["bob"]) print(ages.get("zed"), ages.get("zed", 0)){"ann": 32, "bob": 27, "cy": 40} 327None 0Making maps
Section titled “Making maps”Write key: value pairs in braces. The key and value types are inferred from the
first pair. An empty map needs a type annotation:
fn main(): let squares = {1: 1, 2: 4, 3: 9} var counts: {str: int} = {} for w in ["a", "b", "a", "c", "a"]: if w in counts: counts[w] += 1 else: counts[w] = 1 print(squares, counts){1: 1, 2: 4, 3: 9} {"a": 3, "b": 1, "c": 1}let m = {} with no type is error E0308. A map type
can appear anywhere a type can: fn count(words: [str]) -> {str: int}, a struct
field, a map of arrays {str: [int]}.
Reading and writing
Section titled “Reading and writing”m[k]reads the value fork. A missing key panics.m[k] = vinsertsk, or replaces its value if it’s already there. Replacing keeps the key’s original position.m[k] += 1and the other compound assignments change a value in place. The key must exist.k in mandk not in mtest for a key.m.get(k)returnsSome(value)orNone;m.get(k, default)returns the value ordefault.m.remove(k)removeskand returnstrueif it was there.
fn main(): var m: {str: int} = {} m["z"] = 1 m["a"] = 2 m["z"] = 3 print(m) let first = m.remove("z") let second = m.remove("z") print(first, second, m) m["z"] = 4 print(m){"z": 3, "a": 2}true false {"a": 2}{"a": 2, "z": 4}A key that was removed and inserted again goes to the end.
Missing keys
Section titled “Missing keys”fn main(): let ages = {"ann": 31} print(ages["zed"])panic: key "zed" is not in the map --> main.br:3:11A missing key usually means a bug or bad input. When it’s expected, say so with
get:
fn main(): let prices = {"apple": 3, "pear": 4} let order = ["apple", "kiwi", "pear"] var total = 0 for item in order: match prices.get(item): case Some(p): total += p case None: print("no price for", item) print(total, prices.get("kiwi", 0))no price for kiwi7 0Looping over a map
Section titled “Looping over a map”for k in m walks the keys in insertion order. m.items() gives (key, value)
pairs to unpack:
fn main(): var stock = {"apple": 3, "pear": 0, "fig": 7} for name in stock: if stock[name] == 0: stock.remove(name) for name, n in stock.items(): print(name, n) print(stock.keys(), stock.values(), stock.items())apple 3fig 7["apple", "fig"] [3, 7] [("apple", 3), ("fig", 7)]The loop walks a snapshot of the map taken when it starts, so the body may insert and remove keys. Keys added in the body don’t show up in that loop.
keys(), values() and items() return new arrays, in insertion order.
Key types
Section titled “Key types”Keys can be integers, bool, str, enums, and arrays, tuples and structs made of
those. Floats can’t be keys:
struct Cell: row: int col: int
enum Color: Red Green
fn main(): var grid: {(int, int): str} = {} grid[(0, 1)] = "x" let cells = {Cell(row=1, col=2): 5} let lists = {Color.Red: [1, 2], Color.Green: []} print(grid, cells[Cell(row=1, col=2)], lists[Color.Red]){(0, 1): "x"} 5 [1, 2]fn main(): let m: {float: str} = {}error[E0335]: `float` can't be used as a map key --> main.br:2:12 |2 | let m: {float: str} = {} | ^^^^^^^^^^^^ invalid key type | = note: map keys must be integers, `bool`, `str`, enums, or arrays, tuples and structs of those = help: floats aren't allowed because `nan != nan`; use an integer key instead = run `brine explain E0335` for more about this errorerror: could not compile due to 1 errorA key must be equal to itself for lookups to work, and nan isn’t. To key by a
measurement, round it to an integer first (for example, millimetres instead of
metres).
Maps are values
Section titled “Maps are values”Assigning a map or passing it to a function copies it. The copy is lazy (copy-on-write), so it costs nothing until one side changes:
fn main(): var a = {"x": 1} let b = a a["x"] = 2 a["y"] = 3 print(a, b) print({"p": 1, "q": 2} == {"q": 2, "p": 1}){"x": 2, "y": 3} {"x": 1}true== compares keys and values and ignores order. To let a function change the
caller’s map, take it as ref: fn add(ref m: {str: int}, k: str). See
functions.
par bodies may read maps but not change outer ones. See
parallelism.
Reference
Section titled “Reference”| Operation | Result | Notes |
|---|---|---|
{k1: v1, k2: v2} |
{K: V} |
empty: var m: {K: V} = {} |
len(m) |
int |
number of keys |
m[k] |
V |
panics if k is missing |
m[k] = v |
insert or replace | |
k in m, k not in m |
bool |
|
m.get(k) |
Option[V] |
|
m.get(k, default) |
V |
|
m.remove(k) |
bool |
true if k was there |
m.keys() |
[K] |
insertion order |
m.values() |
[V] |
insertion order |
m.items() |
[(K, V)] |
insertion order |
for k in m |
keys, in insertion order, over a snapshot | |
m == n |
bool |
same keys and values, any order |
- Maps remember insertion order; replacing a value keeps the key’s place.
m[k]panics on a missing key. Useinorgetwhen a key may be absent.- Keys can’t be floats, or contain floats.
let b = acopies; changingbnever changesa.- A loop over a map sees a snapshot, so the body may change the map.
Related: arrays, strings,
generics (maps work with type parameters:
fn invert[K, V](m: {K: V}) -> {V: K}), error handling.