Error Handling

Jda provides multiple mechanisms for error handling: the Result type for recoverable errors, defer for cleanup, panic for unrecoverable errors, and the ? operator for error propagation.

Result Type

Functions that can fail return a Result:

struct Result<T> {
    tag: i64     // 0 = ok, 1 = err
    value: T     // the success value
    error: i64   // error code
}

The ? Operator

The ? operator unwraps a Result — returning the value on success, or propagating the error on failure:

fn read_config(path: &i8) -> Result<;Config> {
    let fd = file_open(path)?         // propagates error if open fails
    defer file_close(fd)
    let data = file_read_all(fd)?     // propagates error if read fails
    let config = parse_config(data)?  // propagates error if parse fails
    ret ok(config)
}

Without ?, you’d need explicit error checking at every step:

fn read_config(path: &i8) -> Result<;Config> {
    let fd_result = file_open(path)
    if fd_result.tag == 1 { ret fd_result }
    let fd = fd_result.value
    // ... and so on for every call
}

Defer for Cleanup

defer ensures cleanup code runs regardless of how the function exits — normal return or early error return via ?:

fn process_file(path: &i8) -> Result<;i64> {
    let fd = file_open(path)?
    defer file_close(fd)           // always runs

    let buf = alloc(4096)
    defer free(buf)                // always runs

    let n = file_read(fd, buf, 4096)?
    ret ok(n)
}

Multiple defers execute in LIFO order:

fn do_work() {
    defer print("3\n")
    defer print("2\n")
    defer print("1\n")
    print("working\n")
}
// Output: working, 1, 2, 3

Panic

For unrecoverable errors, use panic:

fn must_positive(x: i64) -> i64 {
    if x <= 0 {
        panic("expected positive number")
    }
    ret x
}

panic prints a message to stderr and aborts the program with a non-zero exit code.

Error Handling Patterns

Wrapping Errors

fn load_user(id: i64) -> Result<;User> {
    let data = db_query(id)?
    let user = parse_user(data)?
    ret ok(user)
}

Default Values

fn get_port() -> i64 {
    let result = env_get("PORT")
    if result.tag == 1 {
        ret 8080    // default
    }
    ret atoi(result.value)
}

Assert for Invariants

import "assert"

fn divide(a: i64, b: i64) -> i64 {
    assert(b != 0, "division by zero")
    ret a / b
}