Defining Error Types

  type ParseError = error {
    InvalidChar  { ch: u8, pos: usize },
    UnexpectedEof,
    Overflow     { value: i64 },
};
  

Returning Errors

  fn parseU8(s: []const u8): ParseError!u8 {
    if s.len = 0 {
        return error.UnexpectedEof;
    }

    var result: u8 = 0;
    for byte, i in s.bytes() {
        if byte < '0' or byte > '9' {
            return error.InvalidChar { .ch = byte, .pos = i };
        }
        result = result *% 10 +% (byte - '0');
    }
    return result;
}
  

Handling Errors

  -- try: propagate if same error type
var n = try parseU8(input);

-- catch: handle inline
var n = parseU8(input) catch |e| {
    case e {
        .InvalidChar { ch, pos } => {
            debug.print("bad char '{}' at {}\n", .{ch, pos});
            return error.InvalidChar { .ch = ch, .pos = pos };
        },
        .UnexpectedEof => return error.UnexpectedEof,
        .Overflow { value } => {
            debug.print("overflow: {}\n", .{value});
            return 255;
        },
    }
};

-- orelse: provide default on error
var n = parseU8(input) catch 0;
  

Wrapping Errors — wrap

When your function has a different error type:

  type AppError = error {
    wrap ParseError,        -- generates: ParseError { inner: ParseError }
    wrap IoError,
    Config { msg: []const u8 },
};

fn loadConfig(path: []const u8): AppError!Config {
    var f = openFile(path) catch wrap;    -- IoError auto-wrapped
    var text = try f.readAll(alloc);
    return parseConfig(text) catch wrap;  -- ParseError auto-wrapped
}
  

defer and errdefer

  fn processFile(alloc: Allocator, path: []const u8): !Report {
    var f = try openFile(path);
    defer f.close();                    -- always runs

    var buf = try alloc.alloc(u8, 4096);
    errdefer alloc.free(buf);           -- runs only on error

    var n = try f.read(buf);
    return try buildReport(buf[0..n]);  -- success: errdefer skipped
}
  

The unassigned Pattern

  fn sumPositive(items: []const i32): ?i32 {
    var total: ?i32 = unassigned;    -- not yet set, compiler tracks this

    for item in items {
        if item.* > 0 {
            total = if total -> t { t + item.* } else { item.* };
        }
    }

    return total;    -- may be unassigned if no positive items found
                     -- type ?i32 handles this: returns .None
}