Skip to content

Work with raw buffers

NativeBrowserNode2 min read

Use ordinary arrays first. This guide explains the extra care required when building a custom container or using manual memory operations.

At a glance

@unsafe
fun grow(p: RawPtr<int>, count: usize): RawPtr<int> {
    return p.realloc(count);
}
  • Prefer arrays and lists; raw buffers are for custom containers.
  • Manual operations are only callable from other @unsafe code.

Back to overview

Raw buffers and custom containers

T[] is always safe to use directly — including as the backing storage of your own data structure. Three guarantees hold for any array, no matter how you track its contents:

  1. Overwriting a slot releases the old element immediately.
  2. Reading a slot retains the element — both the array and your variable are valid owners, so there is no way to create a dangling or double-freed reference through reads.
  3. Freeing an array releases every slot, including slots a counter has already "left behind".

The practical consequence: if you hand-roll a container (entries: string[] plus a count: int), rewinding the counter without clearing slots is safe. Old elements stay until they are overwritten or the array is dropped. There is no error.

Use List<T> when the logical size changes. Removing an element or calling clear() releases the list's ownership of its elements. When an array needs empty slots, choose an optional element type and assign Option.None to release a slot's previous value.

@unsafe: manual memory management

RawPtr<T> and Arena provide raw storage for unmanaged values. You manage the lifetime of this storage yourself. Every function or method that touches one of these must be marked @unsafe:

@unsafe
fun grow(p: RawPtr<int>, new_count: usize): RawPtr<int> {
    return p.realloc(new_count);
}

fun caller(): void {
    let p = RawPtr<int>.alloc(4);
    grow(p, 8); // error: call to '@unsafe' function 'grow' is only allowed from
// another '@unsafe' function or method
}

Calling an @unsafe function from ordinary code is a compile-time error. Marking your own function @unsafe means its callers must be @unsafe too — the attribute has to be threaded all the way up to wherever the unsafe operation is justified.

@unsafe does not insert runtime checks, and it does not verify the contract of the operation you're calling (e.g. that a freed RawPtr<T> is never read again). It is a documented promise from the author, not a proof.