Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Boxes

This page describes concrete Box[T]. Explicit Box[P](value)? for a protocol creates a protocol object with indirect method dispatch and no payload extraction or copying.

Box(value) -> Result[Box[T], AllocError] moves a value into one heap allocation. Failure drops the input. Boxes are affine. Moving, borrowing, extracting content, and destroying the box add no allocation; explicit copying and operations on its content retain their own costs. Boxes provide a heap boundary for recursive data. A box has one owner; Shared[T] has several.

For insertion into a borrowed structure, allocate a detached node before moving existing contents into it. Otherwise a failed allocation can destroy the old contents as part of its input. The insertion lesson demonstrates preservation without adding a successful-path allocation.

Where a T is required, Box[T] converts to T by moving out the content and freeing the box, without failure. This applies to arguments, returns, annotated bindings, assignments, constructor fields, and collection elements. Borrowed boxes convert to references to their contents. Nested boxes convert through every level.

Without an expected content type, the box remains a box: other = box moves its owner. Operators and conditions do not convert it. *box explicitly extracts the content; &*box and &mut *box borrow it. Matching an owned box consumes it; matching a borrowed box lends its content.

Class fields and methods are reached directly through boxes. Builtin list, dictionary, set, and string methods also reach through nested boxes. Calling a method on a boxed binding, field, or element borrows its content and preserves the box. Mutation requires a mutable owner or exclusive reference.

Read-only methods accept owned temporary boxes, keeping content alive through the expression. Mutation needs a binding, field, or element rather than a temporary receiver. Read-only builtin methods also accept boxed projections from temporary collections or classes: the temporary owner stays alive until the end of the full expression, without copying its content or requiring a named binding:

def main() -> Result[(), AllocError | IoError]:
    print([Box([1, 2]?)?]?[0].get(1))?
    Ok(())
Option[i64].Some(2)

A returned reference to a box still needs a named binding before calling a builtin method directly on it. See returned references for temporary origins and projected receivers.

Automatic copy, equality (including membership), print, and str.repr reject types whose stored values can recur, including through containers or standard sums. Use explicit traversal to inspect such values. Automatic box cleanup allocates nothing and does not recurse on the native stack; user methods and destructor hooks retain ordinary recursion and effect rules.