(you can find previous post about same topic here)
Errors are values
As in Go, errors in Zig are also handled as values, but while in Go we can indicate that a function returns multiple values, so one of them could be a pointer to an error, in Zig instead we declare a sort of union called error union type: the ! symbol that precedes the type returned by a function indicates that we might get an error. We can also have a precise indication of the error type that is defined as an enum if in addition to the ! we also have the type specification.
// Go
func canFail(num int) (int, error) {
if num > 10 {
return num, errors.New("input is greater than max (10)")
}
return num, nil
}
// Zig
fn canFail(num: i8) !i8 {
if (num > 10) {
return error.InputIsGreaterThanMax;
}
return num;
}
We can notice that the substantial differences lie in the fact that Zig returns the value of an enum (declared inline in this example) and that this is not coupled with the result but is mutually exclusive; this is better seen by observing how it is handled.
// Go
result, err := canFail(val)
if err != nil {
fmt.Printf("An error occurs: %v\n", err)
os.Exit(1)
}
// handle result value
// Zig
const result = canFail(num) catch |err| {
std.debug.print("An error occurs: {}\n", .{err});
return err;
};
// handle result value
In Zig it is also possible to use a more concise formula when the error should not be handled but only returned to the previous step:
const result = try canFail(num);
// handle result value
try in this case is the compressed version of catch |err| return err.
Error declaration
In Go, an error is any struct that implements the Error() string method, and to create custom errors we use these approaches.
// Go
var (
ErrMissingArgument = errors.New("missing argument")
ErrInvalidArgument = errors.New("invalid argument")
)
type MaxValueValidation struct {
Max int
Current int
}
func (v *MaxValueValidation) Error() string {
return fmt.Sprintf("input %d is greater than max %d", v.Current, v.Max)
}
In Zig, however, the error is reduced to an enum that can be combined with other enums and the result used as an indication of possible return errors.
const InputError = error{
WrongInput,
MissingInput,
};
const ValidationError = error{
InputGreaterThanMax,
};
const FailureError = InputError || ValidationError;
Here are two complete examples of error handling:
// Go
package main
import (
"errors"
"fmt"
"os"
"strconv"
)
var (
ErrMissingArgument = errors.New("missing argument")
ErrInvalidArgument = errors.New("invalid argument")
)
type MaxValueValidation struct {
Max int
Current int
}
func (v *MaxValueValidation) Error() string {
return fmt.Sprintf("input %d is greater than max %d", v.Current, v.Max)
}
func main() {
args := os.Args
if len(args) < 2 {
fmt.Printf("An error occurs: %v\n", ErrMissingArgument)
os.Exit(1)
}
val, err := strconv.Atoi(os.Args[1])
if err != nil {
fmt.Printf("An error occurs: %v\n", ErrInvalidArgument)
os.Exit(1)
}
result, err := canFail(val)
var validationError *MaxValueValidation
if errors.As(err, &validationError) {
fmt.Printf("Check input: %v\n", validationError)
os.Exit(1)
} else if err != nil {
fmt.Printf("An error occurs: %s\n", err.Error())
os.Exit(1)
} else {
fmt.Printf("The result is %d\n", result)
}
}
func canFail(num int) (int, error) {
if num > 10 {
return num, &MaxValueValidation{Max: 10, Current: num}
}
return num, nil
}
// Zig
const std = @import("std");
const InputError = error{
WrongInput,
MissingInput,
};
const ValidationError = error{
InputGreaterThanMax,
};
const FailureError = InputError || ValidationError;
pub fn main() !void {
var args = std.process.args();
_ = args.skip();
const valueArg = args.next() orelse {
std.debug.print("Error occurs: missing argument.\n", .{});
return FailureError.MissingInput;
};
const num = std.fmt.parseInt(i8, valueArg, 10) catch |err| {
std.debug.print("Error occurs: wrong input {}\n", .{err});
return FailureError.WrongInput;
};
const result = try canFail(num);
std.debug.print("The result is: {d}", .{result});
}
fn canFail(num: i8) FailureError!i8 {
if (num > 10) {
std.debug.print("input {d} is greater than max {d}\n", .{ num, 10 });
return ValidationError.InputGreaterThanMax;
}
return num;
}
Top comments (0)