Silly mistakes (solved)

I get this strange error where zig thinks I haven’t handled an error case, but if I attempt to use try on it, then the compiler complains that I’m using try on a non error value :man_shrugging:

Any help would be appreciated :slight_smile:

I reduced my code down to a minimal example:

const std = @import("std");

/// A combinator parser / functional parser.
pub fn Parser(comptime T: type) type {
    return fn ([]u8) ParseError!struct { T, []u8 };
}

pub const ParseError = error{
    /// The parser could succeed given more input, but ran out.
    Incomplete,
    /// An unrecoverable failure has occurred.
    Panic,
};

/// Matches a string of one or more characters.
pub fn tag(comptime pattern: []const u8) Parser([]const u8) {
    const s = struct {
        fn invoke(rest: []u8) ParseError!struct { []const u8, []u8 } {
            if (rest.len < pattern.len) {
                return ParseError.Incomplete;
            } else {
                for (pattern, 0..) |char, j| {
                    if (rest[j] != char) {
                        return ParseError.Panic;
                    }
                } else {
                    return .{ pattern, rest[pattern.len..] };
                }
            }
        }
    };

    return s.invoke;
}

/// Match either of two given parsers
// zig fmt: off
pub fn either(
    T: type,
    left: Parser(T),
    right: Parser(T)
) Parser(T) {
    // zig fmt: on
    const s = struct {
        pub fn invoke(rest: []u8) ParseError!struct { T, []u8 } {
            if (left(rest)) |result| {
                return result;
            } else |err| {
                switch (err) {
                    ParseError.Incomplete => return err,
                    ParseError.Panic => return right(rest),
                }
            }
        }
    };

    return s.invoke;
}

test "either" {
    // Should match any string of either "foo" or "bar"
    const parse_foobar = either([]const u8, tag("foo"), tag("bar"));

    const foo, var rest = try parse_foobar(@constCast("foo"));
    // error: error union is ignored
    std.testing.expectEqual(rest.len, 0);
    // error: expected error union type, found 'usize'
    // std.testing.expectEqual(try rest.len, 0);
    std.testing.expectEqual(foo, "foo");

    const bar, rest = try parse_foobar(@constCast("bar"));
    std.testing.expectEqual(rest.len, 0);
    std.testing.expectEqual(bar, "bar");
}

In Zig, only function calls are, well, function calls. Access to a field or declaration (rest.len) is not a function call, and cannot fail.

std.testing.expectEqual(...), is a function call, and can fail, that’s what makes it a test.

Ah, I’m an idiot and forgot to put a try in front of std.testing.expectEqual. Thank you @mnemnion.

1 Like