Mako ships a standard library focused on backend development: strings, formatting, file I/O, networking, encoding, cryptography, synchronization, and database clients. This chapter tours the major packages with imports and usage examples.
You can call many helpers as bare builtins (str_split, path_join, ...) or
import packages for namespaced access (strings.split, path.clean, ...).
import "strings"
import "path"
fn main() {
let parts = strings.split("a,b,c", ",")
print(strings.join(parts, "/"))
print(path.clean("/x/../y"))
}
Bare import "strings" resolves from the standard library directory (std/,
overrideable via MAKO_STD). The import auto-aliases so strings.split works
immediately.
The strings package provides operations on string values. Strings in Mako are
owned, heap-allocated, null-terminated byte sequences with a length field.
fn main() {
// Length (bytes)
print_int(len("hello")) // 5
print_int(rune_count("cafe\u0301")) // Unicode code points
// Comparison
if str_eq("a", "a") {
print("equal")
}
// Search
if str_contains("hello world", "world") {
print("found")
}
// Concatenation
let s = "ma" + "ko"
print(s)
// Indexing (byte access)
let c = "hello"[0] // byte value
print_int(int(c)) // 104
// Slicing (by bytes)
print("hello"[1:4]) // "ell"
print("hello"[:2]) // "he"
print("hello"[3:]) // "lo"
}
import "strings"
fn main() {
let parts = strings.split("a:b:c", ":")
print(strings.join(parts, ", ")) // "a, b, c"
print(strings.trim(" hi ")) // "hi"
print(strings.to_upper("mako")) // "MAKO"
print(strings.to_lower("MAKO")) // "mako"
print(strings.replace("aXbXc", "X", "-")) // "a-b-c"
if strings.has_prefix("hello", "he") {
print("yes")
}
if strings.has_suffix("file.mko", ".mko") {
print("mako file")
}
print_int(strings.index("hello", "ll")) // 2
print_int(strings.count("banana", "a")) // 3
}
fn main() {
// Parse
match parse_int("42") {
Ok(n) => print_int(n)
Err(e) => print(e)
}
// Format
print(format_int(123))
print(string(42)) // int to string
}
Format values into strings for output or logging:
import "fmt"
fn main() {
print(format_int(42))
print(format_float(3.14))
print(format_bool(true))
// Sprintf-style (limited)
log_info("request handled")
log_warn("slow query")
log_error("connection failed")
}
Buffered reading and writing for efficient I/O operations:
import "bufio"
fn main() {
let content = read_file("data.txt")
let lines = str_split(content, "\n")
for line in lines {
print(line)
}
}
File system operations, environment variables, and process interaction:
fn main() {
// File I/O
let _ = write_file("/tmp/test.txt", "hello mako")
let body = read_file("/tmp/test.txt")
print(body) // "hello mako"
// Environment
let _ = env_set("APP_MODE", "production")
let mode = env_get("APP_MODE")
print(mode) // "production"
// Command-line arguments
print_int(argc())
if argc() > 1 {
print(arg_get(1))
}
// Exit
// exit(1)
}
Path manipulation (platform-aware joining, cleaning, splitting):
import "path"
fn main() {
let p = path_join("foo", "bar")
print(p) // "foo/bar"
print(path_clean("/a/../b/./c")) // "/b/c"
}
The filepath package adds glob and walk:
import "filepath"
fn main() {
// Walk directory tree
let count = filepath_walk_n("/tmp", 100)
print_int(count)
}
Time measurement and formatting:
fn main() {
let start = now_ms()
sleep_ms(50)
let elapsed = now_ms() - start
print_int(elapsed) // ~50
let formatted = time_format(now_ms())
print(formatted) // human-readable timestamp
}
Numeric operations and constants:
fn main() {
print_int(abs(-42)) // 42
print_int(min(3, 7)) // 3
print_int(max(3, 7)) // 7
// Float math
print_float(sqrt(16.0)) // 4
print_float(pow(2.0, 10.0)) // 1024
}
JSON encoding and decoding. See Chapter 9 for the full JSON API. Quick overview:
fn main() {
// Build JSON objects
let obj = json_ss("name", "Ada", "city", "London")
print(obj) // {"name":"Ada","city":"London"}
// Extract fields
let name = json_get_string(obj, "name")
print(name) // Ada
// From map
let mut m = make(map[string]string, 4)
m["key"] = "value"
let j = json_object_from_map_ss(m)
print(j)
}
fn main() {
let encoded = base64_encode("hello mako")
print(encoded) // aGVsbG8gbWFrbw==
let decoded = base64_decode(encoded)
print(decoded) // hello mako
}
fn main() {
let h = hex_encode("ok")
print(h) // 6f6b
let d = hex_decode(h)
print(d) // ok
}
fn main() {
let row = csv_escape("hello, world")
print(row) // "hello, world" (quoted because of comma)
}
fn main() {
let safe = xml_escape("<tag attr=\"val\">")
print(safe) // <tag attr="val">
}
Cryptographic hashing and AEAD encryption (when OpenSSL is linked):
fn main() {
let h = sha256_hex("hello")
print(h) // 64-char hex digest
let h2 = md5_hex("test")
print(h2) // 32-char hex digest
}
fn main() {
let compressed = gzip_compress("hello world hello world")
print_int(len(compressed))
let original = gzip_decompress(compressed)
print(original)
}
Pattern matching with capture groups:
fn main() {
// Simple match check
if regex_match("[0-9]+", "abc123def") {
print("has numbers")
}
// Capture groups
let version = regex_capture("([0-9]+)-([0-9]+)", "v1-42", 1)
print(version) // "1"
let patch = regex_capture("([0-9]+)-([0-9]+)", "v1-42", 2)
print(patch) // "42"
// Full match (group 0)
let full = regex_capture("([0-9]+)-([0-9]+)", "v1-42", 0)
print(full) // "1-42"
// Named-style capture
let host = regex_capture("https?://([^/]+)", "https://example.com/path", 1)
print(host) // "example.com"
}
Dynamic arrays (slices) with append, index, and iteration:
fn main() {
// Integer slices
let xs = [1, 2, 3, 4, 5]
print_int(len(xs)) // 5
print_int(xs[0]) // 1
// Append
let ys = append(xs, 6)
print_int(len(ys)) // 6
// Slice expression
let sub = xs[1:3] // [2, 3]
for v in sub {
print_int(v)
}
// Make with capacity
let zs = make([]int, 0, 10) // len=0, cap=10
// Sort
let sorted = sort_ints([3, 1, 4, 1, 5])
for v in sorted {
print_int(v)
}
}
Hash maps with string or integer keys:
fn main() {
// String-to-int map
let mut m = make(map[string]int)
m["x"] = 10
m["y"] = 20
print_int(m["x"]) // 10
print_int(len(m)) // 2
// Check existence
if has(m, "x") {
print("has x")
}
// Delete
delete(m, "x")
print_int(len(m)) // 1
// Iterate
for k, v in range m {
print(k)
print_int(v)
}
// String-to-string map
let mut ms = make(map[string]string)
ms["name"] = "mako"
print(ms["name"])
// Int-to-int map
let mut mi = make(map[int]int)
mi[1] = 100
mi[2] = 200
print_int(mi[1])
}
fn main() {
let xs = [5, 2, 8, 1, 9]
let sorted = sort_ints(xs)
print_int(sorted[0]) // 1
let ss = sort_strings(["c", "a", "b"])
print(ss[0]) // "a"
print(ss[1]) // "b"
print(ss[2]) // "c"
}
Context provides deadline and cancellation propagation for operations:
import "context"
fn main() {
// Timeouts for operations use crew cancel or sleep_ms-based patterns
let start = now_ms()
// ... operation with deadline checking ...
let elapsed = now_ms() - start
if elapsed > 1000 {
print("deadline exceeded")
}
}
Synchronization primitives for shared state:
import "sync"
fn main() {
let m = sync.rwmutex()
// Use with crew tasks to protect shared data
print("mutex created")
}
Atomic operations are available for lock-free counters and flags.
CMap is a built-in concurrent hashmap optimized for high-throughput workloads.
It uses lock-free reads and per-stripe spinlock writes (512 stripes, FNV-1a
hash, 1M initial capacity). No import needed -- it is a builtin type.
fn main() {
let m = cmap_new()
cmap_set(m, "greeting", "hello")
print(cmap_get(m, "greeting")) // "hello"
print_int(cmap_has(m, "greeting")) // 1
print_int(cmap_len(m)) // 1
// Atomic counter
let n = cmap_incr(m, "hits", 1)
print_int(n) // 1
let n2 = cmap_incr(m, "hits", 4)
print_int(n2) // 5
// Delete
print_int(cmap_del(m, "greeting")) // 1
print_int(cmap_len(m)) // 1
}
| Builtin | Purpose |
|---|---|
cmap_new() |
Create new concurrent map |
cmap_set(m, key, value) |
Set key-value pair |
cmap_get(m, key) |
Get value ("" if missing) |
cmap_has(m, key) |
Key exists (1/0) |
cmap_del(m, key) |
Delete (returns 1 if existed) |
cmap_len(m) |
Entry count |
cmap_incr(m, key, delta) |
Atomic increment, returns new value |
Safe to share across crew tasks without mutexes or channels. Runtime:
runtime/mako_cmap.h.
Runtime type inspection (limited to struct field names and types):
import "reflect"
fn main() {
// Reflect on struct types at runtime
// Primarily used by derive macros and serialization
}
When importing multiple packages, use grouped import syntax:
import (
"strings"
"path"
"sync"
"fmt"
)
fn main() {
let s = strings.trim(" hi ")
let p = path.clean("/a/../b")
print(s)
print(p)
}
The formatter (mako fmt) automatically rewrites two or more single imports into
a grouped block.
| Area | Packages |
|---|---|
| Text | strings, bytes, strconv, fmt, unicode/utf8, regexp |
| Files | io, fs, path, filepath, bufio, os, os/exec |
| Net | net, http, net/url, net/mail, net/smtp |
| Encoding | json, encoding/*, base64, csv, binary |
| Compress | compress/gzip, archive/tar, archive/zip |
| Crypto | crypto (hashes, AEAD when OpenSSL linked) |
| Sync | sync, sync/atomic, context |
| Data | sql, SQLite/Redis/Postgres clients |
| Other | flag, log/slog, html/text/template, maps, slices, reflect |
Prefer package imports for readable call sites: strings.trim(s) is
clearer than str_trim(s) at the cost of one import line.
Pre-size maps and slices when you know the element count:
mko
let mut m = make(map[string]int, 1000)
let xs = make([]int, 0, 256)
Use arenas for request-scoped buffers that all free together (Chapter 4).
Handle results at boundaries: parsing, I/O, and conversions return
Result types. Match them explicitly.
Method name aliases: where a standard method name collides with a keyword,
Mako uses an alias. For example concat instead of reserved words,
matches instead of match.
import (
"strings"
"path"
)
fn process_config(raw: string) -> string {
let lines = strings.split(raw, "\n")
let mut result = ""
for line in lines {
let trimmed = strings.trim(line)
if len(trimmed) > 0 {
if strings.has_prefix(trimmed, "#") {
// skip comments
} else {
result = result + trimmed + "\n"
}
}
}
return result
}
fn main() {
let config = "# Mako config\nport = 8080\n host = 0.0.0.0\n# end\n"
let cleaned = process_config(config)
print(cleaned)
let base = path_join("config", "app")
let full = path_clean(base + "/../secrets/../app/run")
print(full)
let encoded = base64_encode(cleaned)
print(encoded)
let h = sha256_hex(cleaned)
print(h)
}
For performance-critical file access -- storage engines, databases, log-structured
merge trees -- Mako provides unbuffered (direct) I/O through the dio subsystem.
These functions bypass the standard buffered layer and operate directly on file
descriptors.
fn main() {
// Open, write, sync, close
let fd = file_open("/tmp/dio_demo.dat", 1, 0)
let _ = pwrite(fd, "record-001\n", 0)
let _ = fdatasync(fd)
let _ = file_close(fd)
// Re-open and read back
let fd2 = file_open("/tmp/dio_demo.dat", 0, 0)
let data = pread(fd2, 11, 0)
print(data) // "record-001\n"
let _ = file_close(fd2)
}
Key functions:
| Function | Purpose |
|---|---|
file_open(path, mode, flags) |
Open a file descriptor |
file_close(fd) |
Close descriptor |
pread(fd, count, offset) |
Read at position without seeking |
pwrite(fd, data, offset) |
Write at position without seeking |
file_append(fd, data) |
Append data to file |
fsync(fd) / fdatasync(fd) |
Flush to disk (full / data-only) |
fallocate(fd, size) |
Pre-allocate disk space |
file_size(fd) |
Get file size |
file_truncate(fd, size) |
Truncate file |
file_seek(fd, offset, whence) |
Seek position |
file_read_exact(fd, n) |
Read exactly n bytes |
For random-access patterns (indexes, shared memory, large datasets), use memory-mapped files:
fn main() {
// Create a new mapped file
let m = mmap_create("/tmp/mako_index.dat", 65536)
// Write index entries at known offsets
let _ = mmap_write(m, 0, "idx:0001")
let _ = mmap_write(m, 4096, "idx:0002")
let _ = mmap_sync(m, 0)
// Read back
let entry = mmap_read(m, 0, 8)
print(entry) // "idx:0001"
print_int(mmap_size(m)) // 65536
let _ = mmap_close(m)
}
| Function | Purpose |
|---|---|
mmap_open(path, mode) |
Map an existing file |
mmap_create(path, size) |
Create and map a new file |
mmap_read(m, offset, count) |
Read from the mapping |
mmap_write(m, offset, data) |
Write into the mapping |
mmap_sync(m, flags) |
Flush changes to disk |
mmap_size(m) |
Size of the mapping |
mmap_close(m) |
Unmap and close |
The Buf type enables structured reading and writing of binary data. Use it for
network protocols, file format parsers, and serialization layers.
fn main() {
// Build a binary message
let b = buf_pack_new(128)
buf_write_u8(b, 0x01) // version byte
buf_write_u32(b, 42) // payload length
buf_write_str(b, "hello") // payload
let wire = buf_to_string(b)
buf_free(b)
// Parse the message
let r = buf_from_string(wire)
let version = buf_read_u8(r)
let length = buf_read_u32(r)
let payload = buf_read_str(r, 5)
print_int(version) // 1
print_int(length) // 42
print(payload) // "hello"
buf_free(r)
}
Key operations:
| Function | Purpose |
|---|---|
buf_pack_new(capacity) |
Create a new write buffer |
buf_from_string(s) |
Create a read buffer from bytes |
buf_to_string(b) |
Extract buffer contents |
buf_len(b) / buf_pos(b) |
Total length / read position |
buf_reset(b) / buf_seek(b, pos) |
Reset or seek |
buf_free(b) |
Release buffer memory |
buf_write_u8 ... buf_write_u64 |
Write unsigned ints (LE) |
buf_write_u16be / buf_write_u32be |
Write big-endian |
buf_write_i32 / buf_write_f32 / buf_write_f64 |
Write signed/float |
buf_read_u8 ... buf_read_u64 |
Read unsigned ints (LE) |
buf_read_u16be / buf_read_u32be |
Read big-endian |
buf_read_i32 / buf_read_f32 / buf_read_f64 |
Read signed/float |
buf_write_bytes / buf_write_str |
Write raw data |
buf_read_bytes(b, n) / buf_read_str(b, n) |
Read n bytes |
Big-endian variants are essential for network protocols (which typically use network byte order). Little-endian is the default for on-disk formats on modern hardware.
Next: Networking & HTTP.