Error handling

XS uses try/catch for recoverable errors and panic for unrecoverable ones. defer runs cleanup code regardless of how a function exits.

try / catch / finally

scratch.xs

You can throw any value: strings, numbers, maps, structs. The caught value is whatever was thrown.

scratch.xs

Exceptions propagate up the call stack until caught. Nest try/catch to rethrow with context:

scratch.xs

throw

scratch.xs

panic

panic terminates the process immediately. It prints to stderr and exits with code 1. It is not catchable by try/catch.

panic("fatal: invariant violated")
-- xs: panic: fatal: invariant violated

Related: todo(msg?) panics with "not implemented" and unreachable() panics if somehow reached. Both signal programmer intent rather than runtime conditions.

defer

defer schedules a block to run when the enclosing function returns. Multiple defers execute in LIFO order. Defers run even if an exception is thrown.

scratch.xs
scratch.xs

When to use which

throw / catch for recoverable errors: bad input, missing resources, validation failures. The caller can handle them and continue.

panic for invariant violations: states that should never occur if the program is correct. There is nothing sensible to do except crash and report.

defer for cleanup that must happen regardless of success or failure: closing files, releasing locks, logging completion.