Allocator

Raw allocation callbacks, allocation contracts, custom allocators, and out-of-memory handling.

std.allocator is the raw memory allocation layer used by owning standard library types. Most code should prefer String, Array<T>, MemoryStream, and other typed owners; use Allocator directly when an API needs explicit storage control.

import {
    Allocator,
    AllocError
} from std.allocator;

How It Works

An Allocator stores callbacks for allocation, reallocation, and freeing. Allocator() uses the built-in process allocator. Allocator(alloc, realloc, free) lets tests and low-level code provide a custom strategy.

Allocator methods do not throw. alloc and realloc return null when memory cannot be provided, and higher-level APIs convert that result into AllocError.OutOfMemory.

Use std.arith helpers before calling an allocator when an allocation size is computed from counts, element sizes, sentinels, or growth overhead. Allocation wrappers should convert detected arithmetic overflow into AllocError.OutOfMemory.

Allocation Contract

alloc(size, align) returns a raw pointer or null. alloc(0, align) returns null. The caller owns any non-null pointer and must free it exactly once with the same size and alignment contract.

import std.mem;
import {
    Allocator,
    AllocError
} from std.allocator;

fn main() {
    let allocator = Allocator();
    let ptr = allocator.alloc(4, 1);
    if ptr == null {
        throw AllocError.OutOfMemory;
    }
    defer allocator.free(ptr, 4, 1);

    mem.set(ptr, 7 as u8, 4);
    unsafe {
        assert ptr[0] == 7 as u8;
    }
}

realloc(ptr, old_size, new_size, align) resizes an allocation. Passing null, 0 behaves like allocation, and new_size == 0 frees the allocation and returns null. If growing or moving fails, the original pointer is still owned by the caller.

Custom Allocators

Custom callbacks follow the same contract as the built-in allocator. Thread safety is a property of the callbacks, not of the Allocator value.

fn my_alloc(size: usize, align: usize) -> *u8 { ... }
fn my_realloc(ptr: *u8, old_size: usize, new_size: usize, align: usize) -> *u8 { ... }
fn my_free(ptr: *u8, size: usize, align: usize) -> i32 { ... }

let allocator = Allocator(my_alloc, my_realloc, my_free);

Owned Types

Higher-level types accept Allocator values when their backing storage should come from a specific allocator.

import {
    Array
} from std.collections.array;
import {
    String
} from std.string;
import {
    Allocator
} from std.allocator;

fn main() {
    let text = try String("hello", Allocator());
    let values = Array<i32>(Allocator());

    try values.append(10);
    try values.append(32);

    assert text.str() == "hello";
    assert values.length == 2;
}

API Reference

ItemSignaturePurpose
AllocError.OutOfMemoryerror caseAllocation failed and callers should return or recover through normal error handling.
Allocator()constructorCreate an allocator backed by the built-in process allocator.
Allocator(alloc, realloc, free)constructorCreate an allocator from custom callbacks.
allocalloc(size: usize, align: usize) -> *u8Allocate raw storage or return null.
reallocrealloc(ptr: *u8, old_size: usize, new_size: usize, align: usize) -> *u8Resize raw storage or return null on failure.
freefree(ptr: *u8, size: usize, align: usize)Release an owned allocation.

Fallible APIs should check for null and throw AllocError.OutOfMemory.