ynn
ynn

Reputation: 4817

In Rust, what happens if main function returns Err?

According to The Rust Reference,

If a main function is present, (snip), and its return type must be one of the following:

  • ()

  • Result<(), E> where E: Error

but it doesn't say what happens when main() returns (), Ok(()) or Err(<value>).

As far as I tested,

() Ok(()) Err(<value>)
Exit Status 0 0 1
Additional Behavior - - Error: <value> is printed to stderr

Are these behaviors defined, explicitly explained or guaranteed in some documentation? In particular, can I assume


Notes:


Updates:

Termination trait is finally stabilized in Rust 1.61.0 (source).

Upvotes: 28

Views: 4911

Answers (2)

user2722968
user2722968

Reputation: 16495

This behavior is controlled by the std::process::Termination Trait, which was added in RFC 1937. In particular, the "hidden" lang_start() function - which calls main() - looks roughly like:

fn lang_start<T: Termination>(
    main: fn() -> T,
    argc: isize,
    argv: *const *const u8
) -> !

that is, main() can return any T: Termination. There are implementations of Termination in std for !, (), std::process:ExitCode, and some Result<T, E>-variants where E: Debug. This is why you can return (), Ok(()) and others from main().

To your question, language lawyer mode: The exact behavior of any program that relies on Termination is not strictly specified by the language itself. It is part of the std implementation, not part of the language reference. That means that the same program might behave differently when compiled with different versions of the compiler (binding to different std versions). The exact behavior for printing errors in the Err case is documented but not specified. As RFC 1937 explicitly looks for POSIX-like behavior, you can be reasonably assured that the program will not behave in wildly suprising ways (e.g. exit with status 0 in an Err-case).

Upvotes: 10

Dogbert
Dogbert

Reputation: 222198

The behavior of different return values from main is defined by the std::process::Termination trait:

trait std::process::Termination

A trait for implementing arbitrary return types in the main function.

This trait is documented to return libc::EXIT_SUCCESS on success and libc::EXIT_FAILURE on error.

The default implementations are returning libc::EXIT_SUCCESS to indicate a successful execution. In case of a failure, libc::EXIT_FAILURE is returned.

But those values aren't guaranteed to be 0 and 1 on non-POSIX systems.


As for printing the error message, Termination requires E: Debug and does print the Debug impl to stderr, but I don't believe it's guaranteed to stay exactly the same.

impl<E: fmt::Debug> Termination for Result<!, E> {
    fn report(self) -> ExitCode {
        let Err(err) = self;
        eprintln!("Error: {:?}", err);
        ExitCode::FAILURE.report()
    }
}

Source

Upvotes: 26

Related Questions