Hello World
Here is a complete Zig program. Press Run; it executes in your browser as WebAssembly.
const std = @import("std");
pub fn main(init: std.process.Init) !void {
try std.Io.File.stdout().writeStreamingAll(init.io, "Hello, World!\n");
}Why does main take a parameter?
If you have seen Zig before, you probably expect pub fn main() !void. On
current Zig master, main receives a std.process.Init:
pub fn main(init: std.process.Init) !void
That init carries an Io instance, init.io. This is the same idea as
Allocator, applied to I/O: just as Zig never allocates behind your back, it
now never blocks behind your back either. Any function that touches the
filesystem, network, or clock takes an Io explicitly, so the caller decides
whether that work is threaded, evented, or something else entirely.
The practical consequence is that you cannot reach for a global stdout. You
must be handed the capability to write.
Buffering, and the flush you must not forget
writeStreamingAll is fine for a single fixed string, but real programs format
output. For that you want a buffered writer:
const std = @import("std");
pub fn main(init: std.process.Init) !void {
var buf: [1024]u8 = undefined;
var file_writer = std.Io.File.stdout().writerStreaming(init.io, &buf);
const out = &file_writer.interface;
try out.print("{s} v{d}.{d}\n", .{ "zig", 0, 16 });
for (0..3) |i| try out.print(" line {d}\n", .{i});
// Nothing is written until the buffer is drained.
try out.flush();
}Since Zig 0.15 (the release nicknamed “writergate”), writers are buffered and the buffer is part of the interface, not the implementation. You supply the memory, which means no hidden allocation.
The tradeoff is that nothing is written until you flush. Delete the
try out.flush() line above, press Run, and you will get no output at all. Try
it: click Edit, remove the line, and Run again.