unsafe { ... } authorizes calls and operations marked unsafe:
value := unsafe {
memory := intrinsics.alloc(8, 8)
pointer := intrinsics.ptr_cast[u8, int](memory)
intrinsics.store[int](pointer, 42)
intrinsics.load[int](pointer)
}It is an expression, so its tail value can be bound or returned.
What it changes
Unsafe code may perform operations whose initialization, lifetime, alignment, aliasing, or representation requirements cannot be verified normally. It does not disable parsing, name resolution, generic checking, or all type checking.
The permission is lexical. Calling an unsafe operation requires an enclosing unsafe block even when the surrounding function mostly does low-level work. This keeps the exact proof boundary visible during review.
An unsafe block does not:
- make an invalid cast correct;
- keep an allocation alive;
- pin a movable buffer;
- turn a raw pointer into an owned value;
- allow a raw pointer to cross a suspension point;
- guarantee that a host-provided address is within guest memory.
Pointer kinds
ptr[T] is an arena-relative pointer used by managed runtime code.
rawptr[T] is an absolute WebAssembly linear-memory address for constrained
low-level regions.
Raw pointers cannot remain live across suspension and cannot be stored in arena-relative managed storage. The compiler diagnoses known escapes, but an unsafe block remains responsible for:
- valid allocation and alignment;
- initialized reads;
- correct element type and size;
- no use after release or reallocation;
- compatible aliasing and mutation;
- balanced ownership when bypassing managed wrappers.
ptr[T] arithmetic is still layout-sensitive when it is cast or advanced. Use
intrinsics.size_of[T]() and intrinsics.align_of[T]() instead of repeating
assumed constants.
Suspension
A raw absolute address can become invalid when execution yields and the runtime changes arena state. Complete raw-pointer work within a non-suspending region, then return a managed value:
fn decode_word(bytes: [..]byte): int? {
if bytes.byte_len() < 4 {
return None
}
value := unsafe {
pointer := intrinsics.ptr_cast[u8, u32](bytes.byte_ptr())
intrinsics.load[u32](pointer)
}
return Some(value.to_int())
}Do not call a suspending file, network, task, stream, or clock operation while a raw pointer remains part of the live computation.
Containment
Keep unsafe blocks small and build a safe interface around them:
fn read_header(bytes: [..]byte): Header? {
if bytes.byte_len() < HeaderSize {
return None
}
return unsafe {
// SAFETY: length checked; Header's wire layout is fixed.
decode_header(bytes.byte_ptr())
}
}State the invariant at the boundary, not only inside the implementation. Prefer an existing standard-library wrapper because it already participates in suspension, cleanup, and host ABI rules.
Review checklist
For each unsafe block, reviewers should be able to answer:
- Where did every pointer originate?
- What proves the range and alignment?
- Which value owns the allocation for the whole access?
- Are reads initialized and stores valid for
T? - Can mutation reallocate or alias the same storage?
- Can control flow suspend, return, or raise an error before cleanup?
If the proof cannot be stated locally, move the operation behind a smaller well-tested wrapper.