Markdown 97 lines
# Chapter 12: The Standard Library
Flash ships the seed of its own standard library — written in Flash. The library is one named module, `core`; a program imports it with `use core` and reaches every submodule through it (`core.mem.eql`, `core.list.List`, …).
---
## 1. One Door to Zig
A single confinement rule shapes the whole library: `base` is the only std module allowed to import Zig's `std`. Every other module is pure Flash and imports only `base` and its sibling modules, so the toolchain dependency stays behind one door. The surface deliberately mirrors Zig's `std` spellings, so code can move between the two implementations without rewriting call sites.
The library is not an abstraction layer over the language — it is ordinary Flash code, and its in-module `test` blocks double as usage examples.
---
## 2. The Modules
| Module | What it provides |
| :--- | :--- |
| **`core.mem`** | Slice and memory primitives — `eql`, `indexOf`, `copy`, `set`, a stable `sort`, explicit-endian `readInt` / `writeInt`, and byte views (`asBytes`). |
| **`core.list`** | `List(T)` — the dynamic array. |
| **`core.fmt`** | `allocPrint` / `bufPrint` formatting with compile-time-checked format strings, and overflow-checked `parseInt`. |
| **`core.math`** | Integer bounds: `minInt` / `maxInt`, exact at every bit width. |
| **`core.arena`** | `ArenaAllocator` — everything allocated through it is released by one `deinit`. |
---
## 3. `List(T)` — the Dynamic Array
`List(T)` is a generic function returning a struct — the library's workhorse container. `.empty` initializes without allocating; every growing operation takes the allocator explicitly:
```flash
use core
var s core.list.List(u8) = .empty
defer s.deinit(alloc)
try s.appendSlice(alloc, "flash")
try s.append(alloc, '!')
// .items is the live slice — index, slice, and iterate it directly
ok := core.mem.eql(u8, s.items, "flash!")
```
On allocation failure the list is left untouched and still usable — every error path in the library is proven by an induced-failure test sweep.
---
## 4. Formatting & Parsing
The format string is walked at **compile time**, so an unknown verb or a mismatched argument count is a compile error, not a runtime surprise:
```flash
use core
// Allocating: returns a freshly allocated string
msg := try core.fmt.allocPrint(alloc, "{d} items", .{count})
// Allocation-free: formats into a caller buffer, returns the written prefix
var buf [32]u8 = undefined
line := try core.fmt.bufPrint(&buf, "0x{x}", .{addr})
// Parsing mirrors Zig exactly: sign, base-prefix auto-detect under radix 0,
// '_' separators, overflow-checked accumulation
n := try core.fmt.parseInt(i32, "-128", 10)
```
---
## 5. The Arena Allocator
An arena trades fine-grained `free` for one-shot cleanup: allocations bump a cursor inside chunks of backing memory, and a single `deinit` releases everything at once. The handle it returns is a real allocator, so anything that takes one works through the arena unchanged:
```flash
use core
var arena = core.arena.ArenaAllocator.init(child_alloc)
defer arena.deinit()
alloc := arena.allocator()
// Allocate freely; no individual frees needed
var xs core.list.List(i32) = .empty
try xs.append(alloc, 7)
```
---
## 6. Running the Library's Tests
In the Flash repository, the standard library tests itself through its own `test` blocks:
```sh
zig build test-std # transpile and run the std test suite
```
The std sources are also part of the compiler's bootstrap fixpoint corpus, so every release proves they transpile deterministically.