On this page
article
Chapter 8: Error Handling
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
}