Storing an std.fs.File as the result of a conditional expression?

Hi,
I’m trying to write a basic CLI app that either reads from stdin, or a filename passed on the command line. However, this super basic code doesn’t work using Zig 0.15.2, telling me that I don’t use the result of std.fs.File.stdout() for some reason. What am I doing wrong?

const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    var args = try std.process.argsWithAllocator(allocator);
    defer args.deinit();
    if (!args.skip()) unreachable;
    if (args.next()) |arg| {
        const file = if (std.mem.eql(u8, arg, "-")) {
            std.fs.File.stdout();
        } else {
            std.fs.cwd().openFile(arg, .{});
        };
        _ = file;
    }
}

Compiler output:

test.zig:10:31: error: value of type 'fs.File' ignored
            std.fs.File.stdout();
            ~~~~~~~~~~~~~~~~~~^~
test.zig:10:31: note: all non-void values must be used
test.zig:10:31: note: to discard the value, assign it to '_'
referenced by:
    callMain [inlined]: .zvm/0.15.2/lib/std/start.zig:627:37
    callMainWithArgs [inlined]: .zvm/0.15.2/lib/std/start.zig:587:20
    posixCallMainAndExit: .zvm/0.15.2/lib/std/start.zig:542:36
    2 reference(s) hidden; use '-freference-trace=5' to see all references

Thanks in advance!

In zig blocks {} don’t return values implicitly, you can either label them and use a break eg label: { break :label foo; }

const file = if (std.mem.eql(u8, arg, "-")) file: {
            break :file std.fs.File.stdout();
        } else file: {
            break :file try std.fs.cwd().openFile(arg, .{});
        };

Or you can also generally remove them.

const file = if (std.mem.eql(u8, arg, "-"))
            std.fs.File.stdout()
         else 
            try std.fs.cwd().openFile(arg, .{});

Except labelled blocks, ; will only appear at the end of a statement, and not within the expression. That’s why there is only a ; at the end.

ps: for loops the label appears before the while/for keyword, elsewhere the label is immediately before the {}.

pps: label can appear before the switch keyword, or in individual branches, if the branch has a block. the latter is normal, but the former is a little special, see labelled switch.

4 Likes

Ah, I see! Very nice, thank you! Now the ability to break out of an if statement with a label makes much more sense… :wink:

I think they’re actually correct as far as the semicolon placement goes, and when I think about it it makes sense. Consider this:

const x = if (my_condition) "yes" else "no";

Putting a semicolon after the first string would just be confusing and break the parser.

I explicitly didn’t say anything about ; in relation to labels. Just noted them as not what I was talking about.

But to be pedantic, blocks are expressions (especially labelled blocks), and they are unique in that they are the only expression which can contain ; in the middle of them.
It makes sense, as they are a sequence of statements, which always end with a ;.

So they are an exception to what I was talking about.

You might say that type/function definitions can contain ; even though they are expressions. But the ones that do allow that do so in a {}. Though I concede, they do count as expressions separate from blocks.

like this ‘except foo, blah blah blah’

explicitly states ‘blah blah blah’ is not about ‘foo’

1 Like