Catch 22 with readUntilDelimiterOrEofAlloc

I am trying to use readUntilDelimiterOrEofAlloc, but I seem to be hitting a catch 22 of issues.
Code:

const std = @import("std");
const stdin = std.io.getStdIn().reader();
const print = std.debug.print;

pub fn main() !void {
    const gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    print("Enter a file: ", .{});
    const file_buffer = try stdin.readUntilDelimiterOrEofAlloc(allocator, '\n', 100) catch error.StreamTooLong;
    defer allocator.free(file_buffer.?);
}

I get:

 error: expected error union type, found '?[]u8'
    const file_buffer = try stdin.readUntilDelimiterOrEofAlloc(allocator, '\n', 100) catch error.StreamTooLong;

It works if I remove the catch, but then I cannot handle an error.StreamTooLong.
I’m not really sure what to do from here to be able to handle the error.
Adding an orelse would only really handle a null return and not the error union, and if I can’t use a catch I don’t know how to unroll this into a graceful failure, or allow the user to retry the input.

you’re not using the catch syntax correctly. you might want to write something like catch |err| switch (err) and then put in code to handle the errors you expect to need to ask the user to retry for, for example.

Hi @Alumman, welcome to Ziggit!

There are a few issues with your code.

First, your gpa should be var becuase you are modifiying it by using it.

Second, catch is not really used for catching exceptions, like other languages might use it in a try…catch expression. Catch is used to provide the default value for an expression when an error occurs. So the result of a catch expression should be of the correct type. try is used as shorthand to immediately return an error if it occurs. And you won’t usually use try and catch together. Usually it will be one or the other. Further info: Documentation - The Zig Programming Language

I implemented the changes here:

const std = @import("std");
const stdin = std.io.getStdIn().reader();
const print = std.debug.print;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    print("Enter a file: ", .{});

    // file_buffer will be null if stream too long.
    const file_buffer = stdin.readUntilDelimiterOrEofAlloc(allocator, '\n', 100) catch |err| switch (err) {
        error.StreamTooLong => blk: {
            print("Stream too long!", .{});
            break :blk null;
        },
        else => {
            return err;
        },
    };
    defer {
        if (file_buffer) |buffer| {
            allocator.free(buffer);
        }
    }
}

and it produces the following output:

jeff@jeff-debian:~/repos/untracked$ zig run test1.zig 
Enter a file: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Stream too long!
1 Like

Good response in total and generally correct. One caveat here: catch does not have to strictly return a default value. You can do things like break assignments with it - same with orelse.

while (true) {
    const x: usize = foo() catch break;

    bar(x);
}

We could have also done foo() catch continue too… same with catch return. So you can also emit control flow instructions with catch.

2 Likes

If you want a quick rule of thumb that quickly “catches” (see?) this as a red flag: try and catch will seldom (if ever) go together; you either try an expression or catch its possible error return.

1 Like

Thanks. I see I let my python background leak into my code. So “try” is not required if an error is possible if you already have a catch in place to capture the error value?

1 Like

Yes, try is syntactic sugar for:

foo() catch |err| return err;

Also, I just want to say that I’m happy to see more Python programmers trying Zig - good to have you aboard :slight_smile:

2 Likes

I am also from a python background and make the exact same mistake frequently with try…catch. I am only a two month old zigling :slight_smile: .