This commit is contained in:
2026-01-19 05:43:29 +09:00
parent 40f41a4fd0
commit 0f6fddd794
36 changed files with 1724 additions and 0 deletions

111
guide/language/errors.zig Normal file
View File

@@ -0,0 +1,111 @@
const print = @import("std").debug.print;
const expect = @import("std").testing.expect;
// An error set is like an enum (details on Zig's enums later), where each error in the set is a value.
// There are no exceptions in Zig; errors are values. Let's create an error set.
const FileOpenError = error{
AccessDenied,
OutOfMemory,
FileNotFound,
};
// Error sets coerce to their supersets.
const AllocationError = error{OutOfMemory};
test "coerce error from a subset to a superset" {
const err: FileOpenError = AllocationError.OutOfMemory;
try expect(err == FileOpenError.OutOfMemory);
}
// An error set type and another type can be combined with the ! operator to form an error union type.
// Values of these types may be an error value or a value of the other type.
// Let's create a value of an error union type.
// Here catch is used, which is followed by an expression which is evaluated when the value preceding it is an error.
// The catch here is used to provide a fallback value,
// but could instead be a `noreturn` - the type of return, `while (true)` and others.
test "error union" {
const maybe_error: AllocationError!u16 = 10;
const no_error = maybe_error catch 0;
try expect(@TypeOf(no_error) == u16);
try expect(no_error == 10);
}
// Functions often return error unions. Here's one using a catch,
// where the |err| syntax receives the value of the error.
// This is called payload capturing, and is used similarly in many places.
// We'll talk about it in more detail later in the chapter.
// Side note: some languages use similar syntax for lambdas - this is not true for Zig.
fn failingFunction() error{Oops}!void {
return error.Oops;
}
test "returning an error" {
failingFunction() catch |err| {
try expect(err == error.Oops);
return;
};
}
// `try x` is a shortcut for `x catch |err| return err`,
// and is commonly used where handling an error isn't appropriate.
// Zig's `try` and `catch` are unrelated to try-catch in other languages.
fn failFn() error{Oops}!i32 {
try failingFunction();
return 12;
}
test "try" {
const v = failFn() catch |err| {
try expect(err == error.Oops);
return;
};
try expect(v == 12); // is never reached
}
// `errdefer` works like `defer`,
// but only executing when the function is returned from with an error inside of the `errdefer`'s block.
var problems: u32 = 98;
fn failFnCounter() error{Oops}!void {
errdefer problems += 1;
try failingFunction();
}
test "errdefer" {
failFnCounter() catch |err| {
try expect(err == error.Oops);
try expect(problems == 99);
return;
};
}
// Error unions returned from a function can have their error sets inferred by not having an explicit error set.
// This inferred error set contains all possible errors that the function may return.
fn createFile() !void {
return error.AccessDenied;
}
test "inferred error set" {
//type coercion successfully takes place
const x: error{AccessDenied}!void = createFile();
//Zig does not let us ignore error unions via _ = x;
//we must unwrap it with "try", "catch", or "if" by any means
_ = x catch {};
}
// Error sets can be merged.
const A = error{ NotDir, PathNotFound };
const B = error{ OutOfMemory, PathNotFound };
const C = A || B;
// `anyerror` is the global error set, which due to being the superset of all error sets,
// can have an error from any set coerced to it. Its usage should be generally avoided.