about summary refs log tree commit diff
path: root/lang/zig/error-union.zig
blob: 8ec75b2039d4475cffcd64f6a7fa815c785d8301 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
const std = @import("std");
const maxInt = std.math.maxInt;
const expect = std.testing.expect;

fn charToDigit(c: u8) u8 {
    return switch (c) {
        '0'...'9' => c - '0',
        'A'...'Z' => c - 'A' + 10,
        'a'...'z' => c - 'a' + 10,
        else => maxInt(u8),
    };
}

const ParseError = error{ InvalidChar, Overflow };

pub fn parseU64(buf: []const u8, radix: u8) ParseError!u64 {
    var x: u64 = 0;

    return for (buf) |c| {
        const digit = charToDigit(c);
        if (digit >= radix)
            break error.InvalidChar;
        // x *= radix
        if (@mulWithOverflow(u64, x, radix, &x))
            break error.Overflow;
        // x += digit
        if (@addWithOverflow(u64, x, digit, &x))
            break error.Overflow;
    } else x;
}

test "parse u64" {
    const result = try parseU64("1234", 10);
    expect(result == 1234);
}

fn doAThing(str: []u8) !void {
    const number = parseU64(str, 10) catch |err| return err;
    // The same as
    const number = try parseU64(str, 10);
}

test "error switch" {
    if (parseU64("42069", 10)) |number| {
        expect(number == 42069);
    } else |err| switch (err) {
        error.Overflow => {
            // handle overflow...
        },
        // we promise that InvalidChar won't happen
        // (or crash in debug mode if it does)
        error.InvalidChar => unreachable,
    }
}

test "error union" {
    var foo: anyerror!i32 = undefined;

    // Use compile-time reflection to access the type of an error union:
    comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.payload == i32);
    comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.error_set == anyerror);

    // Coerce values
    foo = 1234;
    foo = error.SomeError;
}

const A = error{
    NotDir,

    /// A doc comment
    PathNotFound,
};
const B = error{
    OutOfMemory,

    /// B doc comment
    PathNotFound,
};

const C = A || B;

fn bar() C!void {
    return error.NotDir;
}

test "merge error sets" {
    if (bar()) {
        @panic("unexpected");
    } else |err| switch (err) {
        error.OutOfMemory => @panic("unexpected"),
        error.PathNotFound => @panic("unexpected"),
        error.NotDir => {},
    }
}

const Error = error { Overflow };

// With an inferred error set
pub fn add_inferred(comptime T: type, a: T, b: T) !T {
    var answer: T = undefined;
    return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}

// With an explicit error set
pub fn add_explicit(comptime T: type, a: T, b: T) Error!T {
    var answer: T = undefined;
    return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}

test "inferred error set" {
    if (add_inferred(u8, 255, 1)) |_| unreachable else |err| switch (err) {
        error.Overflow => {}, // ok
    }
}