-
Notifications
You must be signed in to change notification settings - Fork 3
Error union
An error union holds either a value or an error. In normal usage you'll not encounter standalone error-union objects. Upon access they would be resolved automatically, either producing their assigned values or causing errors to be thrown:
const MathError = error{negative_number};
pub fn getSquareRoot(number: f64) MathError!f64 {
if (number < 0) {
return MathError.negative_number;
}
return @sqrt(number);
}
import { sqrt } from './error-union-example-1.zig';
try {
console.log(`sqrt(36) = ${getSquareRoot(36)}`};
console.log(`sqrt(-36) = ${getSquareRoot(-36)}`};
} catch (err) {
console.log(err.message);
}
sqrt(36) = 6
Negative number
It's possible to have an array of error unions, each representing the outcome of an individual operation:
const std = @import("std");
const MathError = error{negative_number};
pub fn getSquareRoots(allocator: std.mem.Allocator, numbers: []const f64) ![]MathError!f64 {
const results = try allocator.alloc(MathError!f64, numbers.len);
for (numbers, results) |number, *result_ptr| {
result_ptr.* = if (number >= 0) @sqrt(number) else MathError.negative_number;
}
return results;
}
import { getSquareRoots } from './error-union-example-2.zig';
const numbers = [ 1, 2, 3, -4, 5 ];
try {
const sqrts = getSquareRoots(numbers);
for (const [ index, sqrt ] of sqrts.entries()) {
const number = numbers[index];
console.log(`sqrt(${number}) = ${sqrt}`);
}
} catch (err) {
console.log(err.message);
}
sqrt(1) = 1
sqrt(2) = 1.4142135623730951
sqrt(3) = 1.7320508075688772
Negative number
The return type of getSquareRoots()
might look a little odd with its two exclamation marks.
It is an error union of an array of error unions. The outer error union captures potential
out-of-memory error during allocation of the result array. The array itself captures errors of the
individual square-rooting operations. When we loop through the results, we are able to process the
first three numbers. The fourth number causes an exception, terminating the loop.
You can ask the iterator to return an error instead of throwing it by passing
{ error: 'return' }
to entries()
.
import { getSquareRoots } from './error-union-example-2.zig';
const numbers = [ 1, 2, 3, -4, 5 ];
const sqrts = getSquareRoots(numbers);
for (const [ index, sqrt ] of sqrts.entries({ error: 'return' })) {
const number = numbers[index];
console.log(`sqrt(${number}) = ${sqrt}`);
}
sqrt(1) = 1
sqrt(2) = 1.4142135623730951
sqrt(3) = 1.7320508075688772
sqrt(-4) = Error: Negative number
sqrt(5) = 2.23606797749979
JSON.stringify()
converts errors stored within error unions to objects of the form { error: [ERROR MESSAGE] }
:
pub const AuthenticationError = error{
UnknownUserNameOrPassword,
UnverifiedAccount,
TwoFactorFailure,
};
pub const AuthorizationError = error{
InsufficientPriviledges,
TemporarilySuspended,
PermanentlyBanned,
};
pub const LoginResult = struct {
authentication: AuthenticationError!bool,
authorization: AuthorizationError!bool,
};
pub fn login() LoginResult {
return .{
.authentication = true,
.authorization = AuthorizationError.PermanentlyBanned,
};
}
import { login } from './error-union-example-3.zig';
const result = login();
const json = JSON.stringify(result, undefined, 2);
console.log(json);
{
"authentication": true,
"authorization": {
"error": "Permanently banned"
}
}