zig
zig copied to clipboard
make std.os.exit cross-platform and related start.zig changes
It is highly recommended you read the commit descriptions. A lot of information in these commit descriptions.
The main focus here was fixing the fact that std.os.exit
is not appropriately abstracted so that it's cross-platform.
std.os.exit(0)
is not cross-platform. There are operating systems where you can't return an integer (Plan 9), or at least not u8
when exiting (UEFI uses usize
), or anything at all (many older systems). On Plan 9 for example, you return a string as the exit status instead of an integer.
Or maybe you wrote an operating system where you return a data structure as the exit status.
TL;DR we cannot assume u8
as the exit status for all platforms.
The current approach of taking u8
but then fixing it up to make it kind of compatible with the underlying system is not ideal either.
This means there is now std.os.ExitStatus
which is the type of the value that can be returned to the operating system when exiting.
However to make it easier to write cross-platform code without having to deal with std.os.ExitStatus
directly (because the type depends on the OS), the declarations std.os.exit_status_success
and std.os.exit_status_failure
are provided for these two most common situations: 1. exiting and communicating success and 2. exiting and communicating failure.
This abstracts the API so that std.os.exit(0)
still works but, assuming 0 means success here, it's now considered unidiomatic and the recommended pattern is std.os.exit(std.os.exit_status_success)
.
Similarly, instead of std.os.exit(1)
you use std.os.exit(std.os.exit_status_failure)
.
For more specific exit statuses you either ignore cross-platform compatibility or come up with a different exit status for that specific case for each platform that you support.
Exiting a program goes in hand with the return type of the main function, because that is what's returned, so there are also many changes to lib/std/start.zig
, namely:
-
std.start
no longer exists in the public std API surface. -
void
,!void
,noreturn
+ whateverstd.os.ExitStatus
is are now supported as the return types formain
on all platforms. Previously, on the UEFI, error unions weren't supported.
Finally, now if your program returns from main
a value of a type other than void
, !void
, or noreturn
, you might want to use std.os.ExitStatus
instead of u8
.
Basically,
pub fn main() u8 {
return 0;
}
still works as before but the cross-platform way of doing it would be
pub fn main() std.os.ExitStatus {
return std.os.exit_status_success;
}
(assuming that 0 means success).
Uses of std.os
always look kind of low-level though so maybe we can create aliases for these definitions in std.process
(std.process.exit
is also an alias of std.os.exit
).
Or maybe aliases in std.start
?
It is highly recommended you read the commit descriptions. A lot of information in these commit descriptions.
Another API I just thought of would be exit(.success)
, exit(.failure)
, and exit(.{ .other = 5 })
. This however will break everything but it'd be an option as well. Advantage is its much shorter.
Again, this PR as-is isn't supposed to be a breaking change for any of the major platforms.
I just tested it on Plan9 and it seems like it worked:
cpu% cat good.zig
const std = @import("std");
pub fn main() std.os.ExitStatus {
return std.os.exit_status_success;
}
cpu% ./good
cpu% echo $status
cpu% cat bad.zig
const std = @import("std");
pub fn main() std.os.ExitStatus {
return std.os.exit_status_failure;
}
cpu% ./bad
cpu% echo $status
bad 598: failure
cpu%
Ah. On POSIX AFAIU the exit code is pretty much defined as u8
right? Or at least an integer. So in that case I'd have to put this somewhere else. std.process
seems good then. It also addresses what I mentioned in the PR description:
Uses of
std.os
always look kind of low-level though so maybe we can create aliases for these definitions instd.process
(std.process.exit
is also an alias ofstd.os.exit
). Or maybe aliases instd.start
?
This is blocked on #16135 for now.