New to Zig. Having issues understanding the compiler warnings. (Error handling and function returns)

I’m relatively new to coding and I don’t have a CS or SE degree.
I’ve written a program in C, then Rust and now I’m trying to write it in Zig.
I really like how explicit you have to be with everything in Zig and it’s making me feel like I understand more about what the code is actually doing.
However, I’ve been running into a lot of issues and I’m having trouble understanding the compiler.
For this bit of the code, I basically want a function which I can use later to get input from the user and handle any errors.
(I plan to do another function for string input later).
Eventually the getUserInput() function will be used to navigate menus and enter in numbers for calculations.
I think I have a poor understanding of error unions but maybe it’s more than that.

I’m sure this is a simple problem.
But I’m at the point where I need to ask for help.
How do I fix this? Also, what should I work on understanding for not being able to fix this myself.
Here is an example of the code:

const std = @import("std");
const print = std.debug.print;

const InputError = error{
    EmptyInput,
};

const NumberError = error{
    InvalidCharacter,
    Overflow,
};

const ParseError = InputError || NumberError;

fn parseNumber(input_to_parser: []const u8) ParseError!u8 {
    if (input_to_parser.len == 0) return error.EmptyInput;
    return std.fmt.parseInt(u8, input_to_parser, 10);
}

fn getUserInput() !u8 {
    const stdin = std.io.getStdIn().reader();
    var input_buffer: [32]u8 = undefined;
    const user_input = try stdin.readUntilDelimiter(&input_buffer, '\n');

    if (parseNumber(user_input)) |number| {
        return number;
    } else |err| switch (err) {
        error.EmptyInput => print("Empty input not allowed!\n", .{}),
        error.InvalidCharacter => print("Invalid character!\n", .{}),
        error.Overflow => print("Overflow!\n", .{}),
    }
}

pub fn main() !void {
    // getUserInput()
    print("Enter a number: \n", .{});
    const user_selection = getUserInput();
    print("You've entered: {d}\n", .{user_selection});
}

What is it exactly that you need to fix? Please share, what compiler error your’e getting, if that is in fact what’s wrong.

BTW Zig compiler doesn’t do warnings. Only errors.

I’m having a lot of trouble posting on this forum.
I’m pretty sure those arrows didn’t print out correctly.

~/code/zig/ditmco2aas: zig run ditmco2aas.zig 
ditmco2aas.zig:20:20: error: function with non-void return type 'typeInfo(typeInfo(TypeOf
(ditmco2aas.getUserInput)).Fn.return_type.?).ErrorUnion.error_set!u8' implicitly returns
fn getUserInput() !u8 {
                   ^~
ditmco2aas.zig:32:1: note: control flow reaches end of body here
}
^
referenced by:
    main: ditmco2aas.zig:38:28
    callMain: /usr/lib/zig/std/start.zig:524:32
    remaining reference traces hidden; use '-freference-trace' to see all reference traces
/usr/lib/zig/std/fmt.zig:521:17: error: cannot format error union without a specifier (i.e. 
{!} or {any})
                compileError("cannot format error union without a specifier (i.e. {!} or {a
ny})");
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~

If the else branch in getUserInput is taken, no value is returned from the function, but you must return either a u8 or an error.

1 Like

To post code use three ``` characters at the start and end of the block, or just hit the </> “Preformatted text” button on the editor.

1 Like

I can’t even figure out how to properly do that.
How do I print and return the error in a switch case statement?

Thanks a lot! I wasn’t sure what “preformatted text meant”.

Depends what you want to happen. Some options:

fn getUserInput() !u8 {
    // ...
    if (parseNumber(user_input)) |number| {
        return number;
    } else |err| {
        switch (err) {
            error.EmptyInput => print("Empty input not allowed!\n", .{}),
            error.InvalidCharacter => print("Invalid character!\n", .{}),
            error.Overflow => print("Overflow!\n", .{}),
        }
        // return the error we got from parseNumber
        return err;
    }
}
fn getUserInput() !u8 {
    // ...
    if (parseNumber(user_input)) |number| {
        return number;
    } else |err| switch (err) {
        error.EmptyInput => print("Empty input not allowed!\n", .{}),
        error.InvalidCharacter => print("Invalid character!\n", .{}),
        error.Overflow => print("Overflow!\n", .{}),
    }
    // return a default value
    return 0;
}
fn getUserInput() !u8 {
    // ...
    if (parseNumber(user_input)) |number| {
        return number;
    } else |err| switch (err) {
        error.EmptyInput => print("Empty input not allowed!\n", .{}),
        error.InvalidCharacter => print("Invalid character!\n", .{}),
        error.Overflow => print("Overflow!\n", .{}),
    }
    // return some other error
    return error.InvalidNumber;
}

You just need to return something, it’s up to you what gets returned and where.

3 Likes

Interesting.
Return err; gives me an LSP error "use of undeclared identifier ‘err’ ".

If I try " return 0" I get a compiler error.

~/code/zig/ditmco2aas: zig run ditmco2aas.zig 
/usr/lib/zig/std/fmt.zig:521:17: error: cannot format error union without a specifier (i.e. 
{!} or {any})
                @compileError("cannot format error union without a specifier (i.e. {!} or {a
ny})");
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~
referenced by:
    format__anon_4002: /usr/lib/zig/std/fmt.zig:185:23
    print__anon_3623: /usr/lib/zig/std/io/Writer.zig:24:26
    remaining reference traces hidden; use '-freference-trace' to see all reference traces
~/code/zig/ditmco2aas:

I believe I get the same compiler error for trying "return error.InvalidCharacter.

Double check you made the changes correctly. The return err; is inside the else in that example.

You have to handle the possible error returned by getUserInput in main. The easiest way would be to use try:

    const user_selection = try getUserInput();
1 Like

Thanks I didn’t realize the difference in syntax for some of your postings. I’m on a small laptop unfortunately.
It’s working now!
But I definitely need to understand errors better.
I’ll most likely be able to reproduce the program I’ve written in C and rust now.

Do you have any suggestions?

Also, will returning the error always crash the program?
If so, I may need to return -1. (0 or positive returns will be problematic)

Returning an error from main won’t crash the program. Try it out and see.

Nice.
I do want the function to handle as much as possible though.

Thanks for your help squeek!

2 Likes