Error set - chung-leong/zigar GitHub Wiki
Error sets are used by Zig functions to indicate they've failed to perform an operation for one
reason or another. They are like enums, unique integers with assigned meanings. In
JavaScript, they are represented as unique
Error
objects. Typically they form part of an error union returned by a function:
pub const FileOpenError = error{
access_denied,
out_of_memory,
file_not_found,
};
pub const AllocationError = error{
out_of_memory,
};
pub fn fail(reason: u32) !bool {
return switch (reason) {
1 => FileOpenError.access_denied,
2 => FileOpenError.out_of_memory,
3 => FileOpenError.file_not_found,
else => false,
};
}
import { FileOpenError, fail } from './error-set-example-1.zig';
try {
fail(2);
} catch (err) {
console.log(err.message);
console.log(err === FileOpenError.out_of_memory);
}
Out of memory
true
Errors with the same name are represented by the same error object. They can appear in multiple error sets. Error objects are not instances of the error set class containing them (unlike enum items):
import { AllocationError, FileOpenError } from './error-set-example-1.zig';
console.log(FileOpenError.out_of_memory === AllocationError.out_of_memory);
console.log(FileOpenError.out_of_memory instanceof FileOpenError);
console.log(AllocationError.out_of_memory instanceof AllocationError);
true
false
false
Use the in
operator to check whether an error is in an error set instead:
import { AllocationError, fail } from './error-set-example-1.zig';
for (let i = 1; i <= 3; i++) {
try {
fail(i);
} catch (err) {
console.log(`${err.message}: ${err in AllocationError}`);
}
}
Access denied: false
Out of memory: true
File not found: false
As you can see in the example above, Zigar automatically derive error messages by inserting spaces between words of names in snake_case and camelCase.
Error casting
You can obtain the numeric value of an error through casting:
import { AllocationError, FileOpenError } from './error-set-example-1.zig';
console.log(Number(FileOpenError.access_denied));
console.log(Number(FileOpenError.out_of_memory));
console.log(Number(FileOpenError.file_not_found));
console.log(Number(AllocationError.out_of_memory));
28
29
30
29
Note how FileOpenError.out_of_memory
and AllocationError.out_of_memory
give us the same number.
This is because they are the same object.
Errors can be casted into strings as well:
import { AllocationError, FileOpenError } from './error-set-example-1.zig';
console.log(String(FileOpenError.access_denied));
console.log(String(FileOpenError.out_of_memory));
console.log(String(FileOpenError.file_not_found));
console.log(String(AllocationError.out_of_memory));
Error: Access denied
Error: Out of memory
Error: File not found
Error: Out of memory
The reverse is also possible. Strings and numbers can be casted into errors:
import { FileOpenError } from './error-set-example-1.zig';
console.log(FileOpenError(47));
console.log(FileOpenError('Error: Access denied'));
console.log(FileOpenError('access_denied'));
console.log(FileOpenError(42));
[ZigError: Access denied] { number: 28 }
[ZigError: Access denied] { number: 28 }
[ZigError: Access denied] { number: 28 }
undefined
undefined
is returned when there is no matching result.
JSON output
Calling
JSON.stringify()
on a Zig error object would yield a string formatted in a manner commonly used by web
applications:
import { fail } from './error-set-example-1.zig';
try {
fail(3);
} catch (err) {
console.log(JSON.stringify(err));
}
{"error":"File not found"}
This string, once parsed, can be casted back into an error object:
import { FileOpenError } from './error-set-example-1.zig';
console.log(FileOpenError(JSON.parse('{"error":"File not found"}')));
[ZigError: File not found] { number: 30 }
anyerror
anyerror
is a special error set in Zig that contains all errors in a given library. Exporting
it would give you a mean to access all public errors:
pub const FileOpenError = error{
access_denied,
out_of_memory,
file_not_found,
};
pub const HumanError = error{
got_into_crypto_currencies,
ran_out_of_beer,
did_not_know_how_to_use_a_condom,
hung_out_with_clifford_banes,
};
pub const AnyError = anyerror;
import { AnyError } from './error-set-example-2.zig';
for (const [ name, err ] of AnyError) {
console.log(err.message);
}
Access denied
Out of memory
File not found
Got into crypto currencies
Ran out of beer
Did not know how to use a condom
Hung out with clifford banes
Note that making anyerror
public does not make all errors public. If you have a function that
explicitly returns an error union with anyerror
, it's possible that Zigar would receive an error
number that it knows nothing about:
const PrivateError = error{just_being_evil};
pub fn fail() anyerror!bool {
return PrivateError.just_being_evil;
}
import { fail } from './error-set-example-3.zig';
try {
fail();
} catch (err) {
console.log(err.message);
}
Error number does not corresponds to any error in error set anyerror: 37
If you simply leave out the error set type and let the compiler infer the type, that type would be made public implicitly and thus visible to Zigar:
const PrivateError = error{just_being_evil};
pub fn fail() !bool {
return PrivateError.just_being_evil;
}
import { fail } from './error-set-example-4.zig';
try {
fail();
} catch (err) {
console.log(err.message);
}
Just being evil