Skip to content

Memory (std.mem)

The std.mem module provides low-level memory allocation and manipulation functions.

nic
import std.mem;

Allocation Functions

alloc

Allocate raw bytes:

nic
pub fn alloc(size: u64) -> opaque;
nic
let buffer = std.mem.alloc(1024);  // 1KB buffer
defer std.mem.dealloc(buffer);

alloc_one

Allocate space for a single value of type T:

nic
pub fn alloc_one[T]() -> *T;
nic
let ptr = std.mem.alloc_one[i32]();
defer std.mem.dealloc(ptr as opaque);

*ptr = 42;
println(int_to_string(*ptr));

alloc_array

Allocate space for an array of values:

nic
pub fn alloc_array[T](count: u64) -> *T;
nic
let arr = std.mem.alloc_array[i32](100);
defer std.mem.dealloc(arr as opaque);

for i in 0..100 {
    arr[i] = i as i32;
}

alloc_zeroed

Allocate zero-initialized memory:

nic
pub fn alloc_zeroed(count: u64, size: u64) -> opaque;
nic
// Allocate 100 zero-initialized i32s
let arr = std.mem.alloc_zeroed(100, sizeof(i32)) as *i32;
defer std.mem.dealloc(arr as opaque);

Deallocation

dealloc

Free previously allocated memory:

nic
pub fn dealloc(ptr: opaque) -> unit;

Always pair allocations with deallocations, preferably using defer:

nic
let buffer = std.mem.alloc(1024);
defer std.mem.dealloc(buffer);

// Use buffer...
// Automatically freed when scope exits

Reallocation

resize

Resize a previously allocated block:

nic
pub fn resize(ptr: opaque, new_size: u64) -> opaque;
nic
let buffer = std.mem.alloc(100);
// Need more space
buffer = std.mem.resize(buffer, 200);
defer std.mem.dealloc(buffer);

Memory Operations

copy

Copy bytes from source to destination:

nic
pub fn copy(dest: opaque, src: opaque, size: u64) -> unit;
nic
let src = std.mem.alloc(100);
let dst = std.mem.alloc(100);
defer { std.mem.dealloc(src); std.mem.dealloc(dst); }

// Fill src with data...
std.mem.copy(dst, src, 100);

set

Fill memory with a byte value:

nic
pub fn set(ptr: opaque, value: i32, size: u64) -> unit;
nic
let buffer = std.mem.alloc(100);
defer std.mem.dealloc(buffer);

std.mem.set(buffer, 0xFF, 100);  // Fill with 0xFF

zero

Zero out memory:

nic
pub fn zero(ptr: opaque, size: u64) -> unit;
nic
let data = std.mem.alloc(100);
defer std.mem.dealloc(data);

std.mem.zero(data, 100);  // Clear to zeros

Best Practices

Use new and release When Possible

For most allocations, prefer the built-in new and release:

nic
// Preferred for typed allocations
let point = new Point { x: 10, y: 20 };
defer release point;

Use defer for Cleanup

Always use defer to ensure memory is freed:

nic
fn process_data() -> unit {
    let buffer = std.mem.alloc(1024);
    defer std.mem.dealloc(buffer);

    // Even if we return early, buffer is freed
    if some_condition {
        return;
    }

    // Process buffer...
}

Avoid Double-Free

Never free the same pointer twice:

nic
let ptr = std.mem.alloc(100);
std.mem.dealloc(ptr);
// std.mem.dealloc(ptr);  // BUG: double-free!

Check for Null

Allocation can fail (returns null):

nic
let buffer = std.mem.alloc(huge_size);
if buffer == nil {
    println("Allocation failed!");
    return;
}
defer std.mem.dealloc(buffer);

Released under the MIT License.