std.mem¶
Accepted
Accepted for V1 namespace ownership, allocation visibility, and the minimal fixed-buffer allocator needed by allocation-backed V1 examples and tests. Broader concrete allocator APIs are deferred.
std.mem owns standard-library memory utilities, concrete allocator implementations, and allocation policy helpers. The Allocator contract itself is prelude because allocation is needed by Box and dynamic iteration, but concrete policies are ordinary standard-library facilities.
Expected Contents¶
- concrete allocator implementations such as arena, fixed-buffer, page, testing, and general-purpose allocators
- allocator adapters, tracking wrappers, and policy helpers
- memory utility functions that are not core syntax
- APIs for explicit allocation policy in library code
- documentation for allocation visibility conventions used by
std
Concrete allocator implementations belong in std. The Allocator contract is prelude because allocation is needed by Box and dynamic iteration, but policies such as arena, fixed-buffer, page, testing, or general-purpose allocators are standard-library facilities.
V1 accepts one concrete allocator implementation, std.mem.FixedBufferAllocator, so accepted allocation-backed features such as Box, box_dyn, iter_dyn, and iter_dyn_mut can be exercised without inventing local allocator implementations in every runnable example. Testing allocators that report leaks are future standard-library work tracked by CEP-0059: Testing Allocator and Leak Reporting.
Allocator is prelude because accepted core semantics name it directly: Box(T).create(alloc, value), box(T, alloc, value), box_dyn(C, I, alloc, value), iter_dyn(alloc), and iter_dyn_mut(alloc). Code should not need to import std merely to state the allocator contract accepted by these operations.
V1 allocator-taking prelude APIs use static dispatch by default:
alloc: *impl Allocator
std.mem allocator types implement Allocator and may expose ordinary constructors, policy knobs, debugging wrappers, and adapters. A Zig-style runtime-erased allocator adapter may live in std.mem later, but dynamic allocator erasure is not the default prelude Box path. Dynamic iteration uses an explicit erased allocator boundary, alloc: *dyn Allocator, because iter_dyn and iter_dyn_mut are themselves dynamic contract operations.
Fixed Buffer Allocator¶
std.mem.FixedBufferAllocator is the only public concrete allocator accepted in V1:
const FixedBufferAllocator = struct(pub Allocator) {
fn create(buffer: []u8) Self
fn allocate(self: *Self, size: usize, align: usize) *opaque!AllocError
fn deallocate(self: *Self, ptr: *opaque, size: usize, align: usize) void
}
The allocator borrows caller-provided mutable byte storage and owns no operating-system, heap, page, or global allocation resource:
var storage: [4096]u8 = undefined
var alloc = std.mem.FixedBufferAllocator.create(storage)
var boxed = try Box(i32).create(&alloc, 42)
defer boxed.dispose()
Concrete pointers to FixedBufferAllocator may also satisfy erased allocator parameters through the ordinary concrete-pointer-to-*dyn coercion when the expected type requests it:
var dyn_alloc: *dyn Allocator = &alloc
var it = try xs.iter_dyn(dyn_alloc)
defer it.dispose()
Rules:
create(buffer)stores a borrowed slice descriptor; the caller must keep the backing storage alive and mutable for every allocation that depends on the allocator.allocate(size, align)returns aligned storage from the remaining buffer or fails withAllocError.OutOfMemory.- allocation is monotonic for V1; successful
deallocatecalls do not guarantee storage reuse. deallocate(ptr, size, align)must accept any allocation previously returned by this allocator with the original layout, even when frees occur out of allocation order.- passing a pointer from another allocator, a wrong size, or a wrong alignment to
deallocateis illegal behavior under the generalAllocatorcontract. - calling
deallocatemore than once for the same allocation is illegal behavior under the general ownership rules, even when this allocator's physical deallocation is no-op. - the allocator does not report leaks, perform compaction, grow storage, call the operating-system allocator, or provide thread-safety guarantees.
- no reset operation is accepted in V1; code that wants to reclaim the whole buffer creates a fresh allocator value over storage whose dependent allocations are already dead.
The no-reuse rule is intentional. FixedBufferAllocator exists to give V1 a deterministic allocation source for examples and compiler tests, not to define a full policy allocator.
V1 Boundary¶
Beyond FixedBufferAllocator, V1 does not accept public concrete std.mem allocator implementations, constructors, singletons, reset APIs, reusable free lists, thread-safe allocators, page/system allocators, general-purpose allocators, testing allocators, or erased allocator adapters. Examples and APIs that need allocation authority should take alloc: *impl Allocator or alloc: *dyn Allocator explicitly.
Allocation Visibility¶
Catalyst should not allocate implicitly by default. Runtime allocation should be visible through explicit APIs once the language has an allocation model.
Allocation should be visible through:
- explicit APIs
- explicit allocator values or ordinary parameters
- function signatures
- resolved calls and the SIR/IR structure already needed by accepted V1 semantics
V1 does not add broad allocation-effect metadata or checked-region facts to SIR or IR. Performance-critical functions may eventually forbid allocation entirely; future enforcement and restriction metadata are tracked by CEP-0020: Realtime Enforcement Regions.
No Hidden Copies¶
Value and reference semantics should be predictable. The language should avoid convenience features that copy, allocate, retain, retain-count, or share data invisibly.
Borrowed by Default¶
Pointers and slices are borrowed by default. Ownership should be expressed through conventions, explicit resource APIs, or ordinary library types unless a stronger core-language model is justified.
See Reference and Mutability for pointer, slice, value, and ownership rules.