std.memory
The std.memory modules define the Allocator interface and the allocators that implement it. The builtins alloc and free are sugar over the allocator in scope: a function marks a parameter with using to designate it as the ambient allocator for its body, and alloc/free inside that body dispatch to it. With no allocator in scope, they use the heap. See memory in the reference for the language rules and the memory guide for a walkthrough.
Both modules require @paradigm procedural and @paradigm oop internally; importing them from a procedural file works as shown below.
std.memory.allocator
Section titled “std.memory.allocator”@import std.memory.allocatorThe Allocator interface and three allocators that implement it. Choosing the allocator type chooses the implementation that alloc resolves to. A stateful allocator advances in place across calls, since a method takes its receiver by pointer.
| Item | Description |
|---|---|
interface Allocator | alloc(size: int64, align: int64) -> *void and free(p: *void) -> void. |
Heap | The libc backed allocator, and the default. |
heap() -> Heap | A heap allocator value to pass through using. |
FixedBuffer | A bump allocator over a caller buffer, no heap. |
fixed_buffer(base: *raw int8, cap: int64) -> FixedBuffer | A fixed buffer allocator over base. |
Debug | A debug allocator that reports leaks, catches a double free, and poisons freed memory with 0xDD. |
debug() -> Debug | A debug allocator value to pass through using. |
The counters debug_leaks() -> int64 and debug_double_frees() -> int64 are builtins, not module exports: debug_leaks reports how many allocations are not yet freed, and debug_double_frees how many double or invalid frees were seen.
The Allocator interface
Section titled “The Allocator interface”interface Allocator { alloc(size: int64, align: int64) -> *void free(p: *void) -> void}Any type that implements this interface is an allocator, so users can write their own and pass it with using. free must run under the allocator that produced the pointer, since a using scope routes free to the scope’s allocator.
The libc backed heap allocator. This is the default when no allocator is in scope, so an explicit Heap is only needed to pass one through using.
func work(using a: Heap) -> void { p: *int64 = alloc(8) // heap allocation, via the passed allocator free(p)}
func main() -> int32 { h := heap() work(h) return 0}As of 0.3.3, pass the allocator with its concrete type, as here; an interface typed parameter such as using a: Allocator rejects a concrete Heap argument at the call site.
FixedBuffer
Section titled “FixedBuffer”A bump allocator over a caller provided buffer. It touches no heap. Allocations carve forward from the buffer, aligned to the request, and individual frees are a no op. Exhausting the buffer prints fatal: fixed buffer exhausted and aborts rather than handing out memory past its end.
@paradigm procedural
@import std.memory.allocator
// Two eight byte allocations from a 64 byte buffer leave the bump// offset at 16.
func fill(using a: FixedBuffer) -> int64 { p: *int64 = alloc(8) *p = 1 q: *int64 = alloc(8) *q = 2 return a.used}
func main() -> int32 { buf: *raw int8 = alloc_bytes(64) mut fb := fixed_buffer(buf, 64) println(fill(fb)) // 16 free(buf) return 0}FixedBuffer exposes its fields base: *raw int8, cap: int64, and used: int64; reading used shows how far the bump offset has advanced. alloc_bytes is a builtin that returns a raw byte block.
A debug allocator. It tracks each allocation to report leaks and catch a double free, and poisons freed memory with 0xDD. Route a section of code through it with using, then read debug_leaks() and debug_double_frees().
@paradigm procedural
@import std.memory.allocator
// p is never freed, so it leaks. q is freed twice, the second a// double free. The counters report 1 and 1.
func work(using a: Debug) -> void { p: *int64 = alloc(8) *p = 1 q: *int64 = alloc(8) *q = 2 free(q) free(q)}
func main() -> int32 { mut d := debug() work(d) println(debug_leaks()) // 1 println(debug_double_frees()) // 1 return 0}std.memory.arena
Section titled “std.memory.arena”@import std.memory.arenaA bump allocator over one backing buffer. Allocations carve forward from the buffer; individual frees are a no op, and the whole arena is reset or destroyed at once. Pass the arena by pointer so the bump offset persists. Exhausting the buffer prints fatal: arena exhausted and aborts.
| Item | Description |
|---|---|
Arena | The arena struct: base: *raw int8, cap: int64, used: int64. |
arena_new(cap: int64) -> Arena | An arena backed by a heap allocated cap byte buffer. |
arena_alloc(a: *Arena, size: int64) -> *void | Carve size bytes, aligned to 8, and return the pointer. |
arena_reset(a: *Arena) -> void | Roll the offset back to zero, keeping the buffer. |
arena_destroy(a: *Arena) -> void | Free the backing buffer. |
Direct use
Section titled “Direct use”@paradigm procedural
@import std.memory.arena
func main() -> int32 { a: *Arena = alloc(arena_new(1024)) p: *int64 = arena_alloc(a, 8) *p = 7 println(*p) // 7 println((*a).used) // 8 arena_reset(a) println((*a).used) // 0 arena_destroy(a) free(a) return 0}arena_destroy frees the backing buffer, not the Arena struct itself; here the struct was heap allocated with alloc, so it needs its own free.
As an Allocator
Section titled “As an Allocator”Arena also implements Allocator, so it can be passed with using and the alloc builtin dispatches to it. The interface implementation aligns each allocation to the requested alignment rather than the fixed 8 of arena_alloc.
@paradigm procedural
@import std.memory.arena
// Arena passed through `using`. It implements Allocator, so the// builtins alloc and free dispatch to it inside fill.
func fill(using a: Arena) -> int64 { p: *int64 = alloc(8) *p = 1 q: *int64 = alloc(8) *q = 2 return a.used}
func main() -> int32 { a: *Arena = alloc(arena_new(64)) println(fill(*a)) // 16 arena_destroy(a) free(a) return 0}Inside fill, free on an arena pointer is a no op, matching the arena model: nothing needs individual free, and the arena is reset or destroyed as a whole.
See also
Section titled “See also”- Memory guide for when to reach for each allocator.
- Memory reference for
alloc,free,defer, and generational references. - Builtins reference for
alloc_bytes,ptr_add,debug_leaks, anddebug_double_frees.