Why is there no default for std.Io.Threaded.InitOptions.environ for v0.16?

I am new to Zig so I am assuming there is a reason, but while I was following along with the zig book’s chapter on building an HTTP server, I saw that while we were both using 0.16, his init call for std.Io.Threaded only needed an allocator passed in, while mine needed an explicit .environ field for the second InitOptions param.

Book Version

const std = @import("std");
const Server = @import("server.zig").Server;

pub fn main() !void {
    var alloc = std.heap.GeneralPurposeAllocator(.{}){};
    const gpa = alloc.allocator();
    var threaded: std.Io.Threaded = .init(gpa);
    const io = threaded.io();
    defer threaded.deinit();

    const server = try Server.init(io);
    var listening = try server.listen();
    const connection = try listening.accept(io);
    defer connection.close(io);
}

Which gave me the following error:

src/main.zig:28:38: error: expected 2 argument(s), found 1
    var threaded: std.Io.Threaded = .init(gpa);

and when I tried to pass in a default struct, I got the following error:

src/main.zig:28:49: error: missing struct field: environ
    var threaded: std.Io.Threaded = .init(gpa, .{});

After a bit of digging through the v0.16 documentation, I found out I could do the following:

const std = @import("std");
const Server = @import("server.zig").Server;
const Io = std.Io;

pub fn main() !void {
    var alloc: std.heap.DebugAllocator(.{}) = .init;
    defer std.debug.assert(alloc.deinit() == .ok);
    const gpa = alloc.allocator();
//------------------------------------------------------------
    const env = std.process.Environ.empty;
    var threaded: Io.Threaded = .init(gpa, .{.environ = env});
//------------------------------------------------------------
    defer threaded.deinit();
    const io = threaded.io();

    const server = try Server.init(io);
    var listening = try server.listen();
    const connection = try listening.accept(io);
    defer connection.close(io);
}

I found this odd because that was the only value I need to explicitly pass in, and otherwise I could have simply done

var threaded: Io.Threaded = .init(gpa, .{});

Is there a reason the environ field does not have a default? On the off chance that it happened to be overlooked or something, I would like to create an issue and fix it if possible.

Thanks in advance to anyone who can help me out!

1 Like

Unfortunately there is no default value available for passing the environment.
After adding juicy main the environment is not available as global variable.

To be able to spawn processes with the correct environment, add a parameter to your main:

pub fn main(init: std.process.Init.Minimal) !void {
    ...
    var threaded: Io.Threaded = .init(gpa, .{.environ = init.environ});
5 Likes

the environ does have an .empty decl, but you probably want to give it the actual environ you get from juicy main.

It is used for process and terminal things.

1 Like

Thank you, makes much more sense now after reading the PR you linked!

Would you happen to know of any case where you would explicitly use the .empty decl?

I don’t think you ever want to give an Io implementation .empty, it will still work if you did but will affect the behaviour of some things.

For example, it needs PATH to create child processes, threaded does have fallback paths, but that’s implementation specific and currently only for Linux.

You might however want to give a child process an empty environ, though they take an Environ.Map which you can get from an existing environ such as .empty or you could is its init function which starts empty.

2 Likes