Strings and text
Str is an immutable UTF-8 string. Lengths, indexes and slices count
code points, not bytes.
Int main() { Str s = "héllo, wörld"; println(f"{str_len(s)} {str_slice(s, 0, 5)} {str_char_at(s, 1)}"); println(f"{str_to_upper(s)} {str_index_of(s, "w", 0)}"); List(Str) parts = str_split("a,b,c", ","); println(f"{parts} {str_join(parts, "-")}"); println(str_replace("2026-09-27", "-", "/")); return 0;}12 héllo 233HÉLLO, WÖRLD 7["a", "b", "c"] a-b-c2026/09/27The string functions are str_len, str_slice, str_char_at (a code
point, as Int), str_from_code, str_index_of, str_contains,
str_starts_with, str_ends_with, str_count, str_split, str_join,
str_trim, str_to_upper, str_to_lower, str_replace, str_repeat,
str_reverse, str_is_int, str_parse_int, str_is_float and
str_parse_float. + concatenates, == compares, and <, <=, >,
>= order strings by code point.
f-strings
Section titled “f-strings”f"...{expr}..." is how any value becomes text. A hole takes a value of
any type:
type User = { Str name; List(Str) roles; };
Int main() { User u = User {.name = "ada", .roles = ["admin", "dev"]}; Dec price = 12.50m; println(f"{u.name} has {u.roles.len()} roles"); println(f"{u}"); println(f"{price} {price:trim} {1.0 / 3.0} {Some(2)}"); return 0;}ada has 2 rolesUser { name: "ada", roles: ["admin", "dev"] }12.50000000000000000000000000000000 12.5 0.3333333333333333 Some(2)Numbers print in full (a Float prints the shortest text that reads back
as the same value, a Dec all its digits; trim drops a Dec’s trailing
zeros). Lists, maps, records and variants print structurally, strings inside
them quoted. A Show behavior replaces the
built-in text for a type.
Format specs
Section titled “Format specs”A hole can carry a spec after :, and the spec is a list of flags in any
order — one rule to remember, no positional mini-language:
Int main() { Int n = 1234567; Int m = 255; Float pi = 3.14159265; Float share = 0.125; println(f"|{n:12}|{n:12, left}|{n:12, center}|"); println(f"|{n:group}|{n:hex}|{m:hex, upper}|"); println(f"|{pi:precision 2}|{share:percent}|"); println(f"|{m:zero, width 8, plus}|"); println(f"|{{not a hole}}|"); return 0;}| 1234567|1234567 | 1234567 ||1,234,567|12d687|FF||3.14|12.5%||+0000255||{not a hole}|width/8, left/right/center, fill 'x'/zero, plus/space/
minus, hex/oct/bin, upper/lower, group, precision 2/.2,
decimal/scientific/percent/auto, and trim. The
reference page has the full table.
A flag a value’s type cannot mean is an error rather than a silent no-op,
so a typo never reads as “it just didn’t apply”. And a spec only changes
how text is written, so when the value is known the whole thing reduces at
compile time — f"{n:group}" with a known n is a literal.
Building strings
Section titled “Building strings”s + t copies both strings. To build a long string from many pieces, use
StrBuf, which appends in place:
StrBuf csv_row(List(Int) xs, Int i, StrBuf acc) { if (i >= xs.len()) { return acc; } StrBuf a = if (i > 0) { acc.push_char(',') } else { acc }; return csv_row(xs, i + 1, a.push(f"{xs[i]}"));}
Int main() { println(csv_row([1, 2, 3, 5, 8], 0, StrBuf()).finish()); return 0;}1,2,3,5,8Other literals
Section titled “Other literals”r"C:\no\escapes" is a raw string. 'a' is a character literal (a code
point Int). b"bytes" is a byte string (Bytes).
