Compiler ignoring switch branches that can error

I have some code that uses a switch to call function A or B based on some input, and a variable to capture the output from the switch. The functions can error, so they’re called with try.

Here’s a minimal example:

const std = @import("std");

const SomeError = error{TheError};
const Orientation = enum { Horizontal, Vertical };

fn dontProvideZero(input: u8) !u8 {
    if (input == 0) {
        return SomeError.TheError;
    } else {
        return input;
    }
}

fn dontProvideOne(input: u8) !u8 {
    if (input == 1) {
        return SomeError.TheError;
    } else {
        return input;
    }
}

pub fn main() !void {
    const input = 5;
    const orientation = Orientation.Horizontal;
    const output = switch (orientation) {
        .Horizontal => {
            try dontProvideZero(input);
        },
        .Vertical => {
            try dontProvideOne(input);
        },
    };
    _ = output;
}

When I compile this, I get:

scratch.zig:27:13: error: value of type 'u8' ignored
            try dontProvideZero(input);
            ^~~~~~~~~~~~~~~~~~~~~~~~~~
scratch.zig:27:13: note: all non-void values must be used
scratch.zig:27:13: note: to discard the value, assign it to '_'
referenced by:
    main: /opt/homebrew/Cellar/zig/0.14.1/lib/zig/std/start.zig:660:37
    comptime: /opt/homebrew/Cellar/zig/0.14.1/lib/zig/std/start.zig:58:30
    2 reference(s) hidden; use '-freference-trace=4' to see all references

And I don’t understand because I am using the output below the switch. What am I missing?

dontProvideZero returns u8, but you are ignoring the return value.

If you want to ignore the return value, you can use:

_ = try dontProvideZero(input);

Hello,
as @dimdin said, the compiler complains because you ignore the value from your function call, but i see your intent to asign to output based on the switch.
The problem is that you created a scope {} inside the switch case options.
Since your inner switch expression is a single line you can just not use scope and do something like this:

pub fn main() !void {
    const input = 5;
    const orientation = Orientation.Horizontal;
    const output = switch (orientation) {
        .Horizontal => try dontProvideZero(input),
        .Vertical => try dontProvideOne(input),
    };
    _ = output;
}

Or if you really need the scope, because the value compute requires multiple operations, you can create the scope, label it and then break from it. (by breaking from scope it returns a value)
Like this:

pub fn main() !void {
    const input = 5;
    const orientation = Orientation.Horizontal;
    const output = switch (orientation) {
        .Horizontal => hor: {
            const tmp = try dontProvideZero(input);
            // many more lines
            break :hor tmp;
        },
        .Vertical => ver: {
            break :ver try dontProvideOne(input);
        },
    };
    _ = output;
}

If is something unclear, feel free to ask more.

Robert :blush:

5 Likes

Ah ha! Yes this is exactly it. Thank you. I wasn’t aware that the “unused” compiler check stopped at the scope boundary, but that makes sense now that I see it.

1 Like

it’s not that it stops the check, but rather that scopes don’t return values without explicitly doing break :label ..., the value really wasn’t being used.

1 Like