# 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.