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

Preserve a chain when insertion fails

A borrowed collection should usually keep its existing contents when insertion fails. Allocate the new node before detaching the old head:

class Node:
    value: i64
    next: Option[Box[Node]]

class Chain:
    head: Option[Box[Node]]

def push(chain: &mut Chain, value: i64) -> Result[(), AllocError]:
    mut node = Box(Node(value, Nothing))?
    node.next = replace(chain.head, Nothing)
    chain.head = Some(node)
    Ok(())

def main() -> Result[(), AllocError | IoError]:
    mut chain = Chain(Nothing)
    push(&mut chain, 10)?
    push(&mut chain, 20)?
    match &chain.head:
        case Some(head):
            print(head.value)?
        case Nothing:
            print("empty")?
    Ok(())
20

The only allocation is Box(...). After it succeeds, replace transfers the old head and leaves Nothing behind; linking the nodes cannot fail or allocate. If allocation fails, the existing chain has not changed.

Putting replace(chain.head, Nothing) inside the Box argument would detach the chain first. Failed box allocation destroys its input, including that old chain. Memory safety does not imply that a failed operation preserves data. For a generic payload, also decide whether failure should destroy the incoming value or return it to the caller.

The earlier consuming construction example abandons the whole local computation on failure. This example has a stronger contract because the caller retains the collection. See the complete linked-stack operations.