When I first encountered Rust, I was annoyed that main()
didn’t take command-line arguments and return an exit code, it seemed gratuitously limited. When I learned how serious Rust was about cross-platform support, it made a lot of sense: for example, Unix gives each process an array of command-line arguments, but Windows gives each process a single big string. Unix lets each process return a u8
exit code, while Windows exit codes are u32
. Embedded platforms often don’t have a concept of “arguments” or “exit codes” at all.
It seems dishonest (in a way the Rust stdlib has avoided dishonesty in the past) to give main()
some particular signature when we know it may not be sensible or even possible on every platform, in the way that C and C++ have done. On the other hand, because of C, most platforms will have some story for command-line arguments and exit codes, so maybe it’s not a terrible idea.
Perhaps we can have per-platform main wrappers in the stdlib. For example, std::os::unix::main_wrapper()
:
fn<F> main_wrapper(f: F) -> ()
where F: FnOnce(args: &[&str], environ: &[&str]) -> Result<u8, Error>
…and also std::os::windows::main_wrapper()
:
fn<F> main_wrapper(f: F) -> ()
where F: FnOnce(args: &str) -> Result<u32, Error>
…and maybe even std::os::generic_main_wrapper()
:
fn<F> generic_main_wrapper(f: F) -> ()
where F: FnOnce() -> Result<u8, Error>
…that does not promise your exit code will actually go anywhere, but it should be implementable everywhere.
…and then code examples could look like:
use std::os::unix;
fn main() {
unix::main_wrapper(|_. _| {
"sasquatch".parse::<u32>();
})
}
That’s a bit of extra boiler-plate, but hopefully not too difficult to explain or hand-wave in introductory texts… especially if the generic main_wrapper()
is in the prelude.