Zig Try and Catch
Try and catch are the keywords that handle an error union where it is called. Try unwraps the value or sends the error to the caller, while catch swaps the error for a fallback.
Try unwraps a good value
The division succeeds, so try takes the number out of the union. The print shows the unwrapped number.
const std = @import("std");
const MathError = error{DivideByZero};
fn divide(a: i32, b: i32) MathError!i32 {
if (b == 0) {
return error.DivideByZero;
}
return @divTrunc(a, b);
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const answer = try divide(20, 4);
try out.print("{d}\n", .{answer});
}Catch swaps an error for a fallback
This division fails, so catch throws the error away. The fallback 0 prints instead.
const std = @import("std");
const MathError = error{DivideByZero};
fn divide(a: i32, b: i32) MathError!i32 {
if (b == 0) {
return error.DivideByZero;
}
return @divTrunc(a, b);
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const answer = divide(20, 0) catch 0;
try out.print("{d}\n", .{answer});
}Catch can name the error it caught
The capture |err| hands the error to the fallback. Here the fallback is the error name itself.
const std = @import("std");
const LookupError = error{NotFound};
fn wordFor(n: i32) LookupError![]const u8 {
if (n == 1) {
return "one";
}
return error.NotFound;
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const word = wordFor(9) catch |err| @errorName(err);
try out.print("{s}\n", .{word});
}Example
const std = @import("std");
const MathError = error{DivideByZero};
fn divide(a: i32, b: i32) MathError!i32 {
if (b == 0) {
return error.DivideByZero;
}
return @divTrunc(a, b);
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const good = try divide(36, 6);
const bad = divide(36, 0) catch -1;
try out.print("{d}\n", .{good});
try out.print("{d}\n", .{bad});
}Key points
- Try: try expr unwraps the value when the call succeeds.
- Propagation: try expr returns the error to the caller at once when the call fails.
- Caller rule: try only works inside a function that can itself return an error.
- Catch: expr catch fallback uses the fallback value when the call fails.
- Capture: catch |err| names the error so the fallback can use it.
- Error name: @errorName(err) turns a captured error into its bare name.
In this example, the first call divides 36 by 6 and try unwraps the result 6. The second call divides by zero and fails. Catch replaces that error with the fallback -1, which prints on the second line.
Note
main is written as pub fn main() !void in this course, so try can pass an error out of main.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
In main, divide(8, 0) returns error.DivideByZero. What does the line const v = divide(8, 0) catch 99; store in v?