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

Store different captured callbacks

Concrete closures remember where they were defined. To put different closures behind one signature, explicitly allocate a BoxedCallable. Its constructor consumes the callback and returns Result, so ? handles allocation failure.

def choose(k: i64) -> Result[BoxedCallable[[i64], Result[i64, OverflowError[i64]]], AllocError]:
    if k > 0:
        add = def [k](value: i64) -> Result[i64, OverflowError[i64]]:
            Ok(k + value)
        return BoxedCallable(add)
    span = range(10, 13)
    multiply = def [span](value: i64) -> Result[i64, OverflowError[i64]]:
        Ok(span[1] * value)
    BoxedCallable(multiply)

def main() -> Result[(), AllocError | IoError | OverflowError[i64]]:
    callbacks: dict[str, BoxedCallable[[i64], Result[i64, OverflowError[i64]]]] = {
        "add": choose(3)?,
        "multiply": choose(0)?,
    }?
    print(callbacks["add"](4)?)?
    print(callbacks["multiply"](4)?)?
    borrowed = &callbacks["add"]
    print(borrowed(5)?)?
    Ok(())
7
44
8

The factory’s branches capture different data but return the same boxed signature. Calling borrows the stored owner, so repeated calls keep it registered and require no extra allocation. Ordinary moves transfer ownership, and dropping or replacing the owner drops its captures. Named functions can also use BoxedCallable(name).

This type currently accepts reusable shared-call callbacks with owned captures. Keep mutable, one-shot, borrowed, or generator-containing closures concrete. Callbacks returning references and worker transfer are also unsupported. Boxing does not make a callback copyable or comparable; use names as registry keys.