API changes applied when upgrading from Zig 0.14/0.15 to 0.16.
std.heap.GeneralPurposeAllocator was renamed to std.heap.DebugAllocator.
// before
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// after — or better yet, use init.gpa (see main() section below)
var gpa = std.heap.DebugAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();The preferred pattern is now to accept std.process.Init as the first parameter. This gives you a pre-configured GPA, an arena allocator, parsed environment variables, an Io handle, and the command-line args — no manual setup required.
// before
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// after
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa; // debug allocator in Debug/ReleaseSafestd.process.Init fields used in this project:
| Field | Type | Purpose |
|---|---|---|
init.gpa |
Allocator |
General-purpose allocator (leak-checked in debug builds) |
init.arena |
*ArenaAllocator |
Arena that lives for the whole process |
init.io |
std.Io |
I/O handle required by Dir, File, and Child operations |
init.environ_map |
*Environ.Map |
Parsed environment variables |
init.minimal.args |
std.process.Args |
Command-line arguments |
process.argsAlloc / process.argsFree were removed.
// before
const args = try process.argsAlloc(allocator);
defer process.argsFree(allocator, args);
// after (arena owns the memory; no defer needed)
const args = try init.minimal.args.toSlice(init.arena.allocator());toSlice requires an arena-style allocator because the returned slices may point into the arena.
process.getEnvVarOwned was removed.
// before
const val = try process.getEnvVarOwned(allocator, "KEY");
defer allocator.free(val);
// after (no allocation; returns a slice into the map's memory)
const val = init.environ_map.get("KEY") orelse return error.EnvironmentVariableNotFound;std.posix.write / STDOUT_FILENO were removed from the public API.
// before
_ = try std.posix.write(std.posix.STDOUT_FILENO, buf);
// after
try std.Io.File.stdout().writeStreamingAll(init.io, buf);std.fs.cwd() and the methods on std.fs.Dir moved to std.Io.Dir. Most operations now take an io argument.
// before
fs.cwd().access(path, .{})
fs.cwd().makePath(path)
// after
std.Io.Dir.cwd().access(init.io, path, .{})
std.Io.Dir.cwd().createDirPath(init.io, path) // makePath renamedstd.fs.path.join and the rest of std.fs.path are still accessible (though deprecated in favour of std.Io.Dir.path).
process.Child.init / child.spawn() were replaced by process.spawn.
// before
var child = process.Child.init(&.{ "git", "clone", url, dest }, allocator);
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
try child.spawn();
// after
var child = try process.spawn(init.io, .{
.argv = &.{ "git", "clone", url, dest },
.stdin = .ignore,
.stdout = .pipe,
.stderr = .pipe,
});
defer child.kill(init.io); // idempotent; safe to call after wait()File.read(&buf) → usize was replaced by scatter/gather streaming reads. End-of-stream is now signalled via error.EndOfStream rather than a zero return.
// before
while (true) {
const n = file.read(&buf) catch break;
if (n == 0) break;
process(buf[0..n]);
}
// after
while (true) {
const vecs: [1][]u8 = .{buf[0..]};
const n = file.readStreaming(io, &vecs) catch break; // EndOfStream → break
process(buf[0..n]);
}child.wait() now takes io, and the Term enum variants are lowercase.
// before
const term = try child.wait();
switch (term) {
.Exited => |code| { ... },
else => ...,
}
// after
const term = try child.wait(init.io);
switch (term) {
.exited => |code| { ... },
else => ...,
}