Error handling
Langoost handles recoverable errors with try / catch / throw, and runs
cleanup with defer.
try / catch
Wrap code that might fail in try, and handle the thrown value in catch. The
catch clause binds the thrown value to a variable:
try {
let data = io.read("config.json")
process(data)
} catch err {
println("failed: " + toString(err))
}
finally
An optional finally block runs on every exit path — normal completion, after
a catch, and even when the catch itself re-throws (the value still unwinds
after finally runs):
try {
work()
} catch err {
println("error: " + toString(err))
} finally {
cleanup() // always runs
}
Use finally for cleanup tied to a single block; use defer (below) for
cleanup tied to the whole function.
throw
Raise an error with throw. You can throw any value; an object is a convenient
way to carry structured detail:
fn parsePort(s: string): int {
let n = toInt(s)
if n <= 0 {
throw {kind: "ParseError", message: "invalid port: " + s}
}
return n
}
An uncaught throw propagates up the call stack; if it reaches the top it
aborts the program with the error.
defer
A defer statement schedules a function call to run when the enclosing function
returns — whether it returns normally or unwinds from a throw. Deferred calls
run in LIFO (last-in, first-out) order, and their arguments are evaluated
immediately:
fn copyFile(src: string, dst: string) {
let input = io.stream.open(src, "r")
defer io.stream.close(input) // always runs on return
let output = io.stream.open(dst, "w")
defer io.stream.close(output)
io.stream.write(output, io.stream.readAll(input))
}
runtime helpers
The runtime module complements these keywords:
runtime.error.new(msg)builds a structured error string, withruntime.error.message(e)andruntime.error.isError(v)to inspect it.runtime.panic.throw(msg)prints to stderr and exits with code 2 — for unrecoverable conditions rather than expected failures.
See the runtime module for details.