85

In Rust the main function is defined like this:

fn main() {

}

This function does not allow for a return value though. Why would a language not allow for a return value and is there a way to return something anyway? Would I be able to safely use the C exit(int) function, or will this cause leaks and whatnot?

7
  • 4
    See this recent post on reddit too. Commented Jun 16, 2014 at 13:52
  • 2
    C’s exit is not good inasmuch as it does not run destructors. The main side-effect this will have is that buffers will not be flushed. e.g. if you have written to stdout or stderr your output may never be written, as they are buffered. Commented Jun 16, 2014 at 13:57
  • 7
    It can be more serious than that. For example, in certain situations you might end up being unable to bind to a TCP port that you had bound for a minute or two (until a timeout in the kernel expires). Commented Jun 16, 2014 at 14:00
  • 1
    @Mike: that’s system buffers. Where buffering is done in user space (e.g. BufWriter), exit will not flush the buffers. Commented Dec 17, 2015 at 0:59
  • 1
    Also this RFC discussion: github.com/rust-lang/rfcs/issues/1176. Commented Jan 5, 2016 at 21:10

6 Answers 6

89

As of Rust 1.26, main can return a Result:

use std::fs::File;

fn main() -> Result<(), std::io::Error> {
    let f = File::open("bar.txt")?;

    Ok(())
}

The returned error code in this case is 1 in case of an error. With File::open("bar.txt").expect("file not found"); instead, an error value of 101 is returned (at least on my machine).

Also, if you want to return a more generic error, use:

use std::error::Error;
...

fn main() -> Result<(), Box<dyn Error>> {
   ...
}
Sign up to request clarification or add additional context in comments.

2 Comments

@Lucretiel not really, since it doesn't say why Rust does not have a return value for main (by default), and this solution doesn't tell how to specify one's own return value (the latter by possibly creating one's own error).
@9769953 While valid criticisms (of your own answer at that!) I marked it as the correct answer anyway. The question was written at a time there was no way to do this, now there is. It is also one of the top results when searching for returning from main, and this answer is the most useful to people quickly looking for an answer.
35

This is no longer the recommended way to exit with a status, see other answers about returning from main and types that implement Termination


std::process::exit(code: i32) is the way to exit with a code.


Rust does it this way so that there is a consistent explicit interface for returning a value from a program, wherever it is set from. If main starts a series of tasks then any of these can set the return value, even if main has exited.

Rust does have a way to write a main function that returns a value, however it is normally abstracted within stdlib. See the documentation on writing an executable without stdlib for details.

As noted in the functions docs, this will exit immediately without running any destructors, so should be used with care:

Note that because this function never returns, and that it terminates the process, no destructors on the current stack or any other thread’s stack will be run. If a clean shutdown is needed it is recommended to only call this function at a known point where there are no more destructors left to run.

5 Comments

even if main has exited - I was under the impression that when the main thread exits, the entire program exits. How could one exit from main without the program exiting?
I'm not entirely sure, I was going off of this post and just updating this hoping to start a discussion about it while I researched a bit more.
I suspect this was from when rust used green threading. Now rust just uses OS threads, and the whole process does exit when the main function does, probably with 0 exit code by default.
Also note that this will immediately exit the process, will not rewind the stack thus will not gracefully shutdown. Drops will not be called.
The doc also say: "referably, simply return a type implementing Termination (such as ExitCode or Result) from the main function and avoid this function altogether:"
16

Try:

use std::process::ExitCode;

fn main() -> ExitCode {
  ExitCode::from(2)
}

Take a look in doc

or:

use std::process::{ExitCode, Termination};

pub enum LinuxExitCode { E_OK, E_ERR(u8) }

impl Termination for LinuxExitCode {
   fn report(self) -> ExitCode {
     match self {
       LinuxExitCode::E_OK => ExitCode::SUCCESS,
       LinuxExitCode::E_ERR(v) => ExitCode::from(v)
     }
   }
}

fn main() -> LinuxExitCode {
    LinuxExitCode::E_ERR(3)
}

2 Comments

This is better than std::process::exit since destructors are executed. I am particularly worried about flushing logs.
Thanks. On Windows, DOS reports the %ERRORLEVEL% as defined. 2 in this case, I changed to something different and it reported that. (PS: If you re-type %ERRORLEVEL% and press ENTER, you will get 9009, because it was the value of %ERRORLEVEL% that DOS CLI executed and correctly reported "not recognized as an internal or external command", which is 9009).
6

The reddit thread on this has a "why" explanation:

Rust certainly could be designed to do this. It used to, in fact.

But because of the task model Rust uses, the fn main task could start a bunch of other tasks and then exit! But one of those other tasks may want to set the OS exit code after main has gone away.

Calling set_exit_status is explicit, easy, and doesn't require you to always put a 0 at the bottom of main when you otherwise don't care.

2 Comments

You don't have to put explicit return 0; in C++ either (unlike C, where not returning a value from a function returning int is UB in all cases, even for main). The Rust's solution seems similar, just with another syntax to explicitly return a value.
From C99 the main function behaves as if there was a return 0; statement at the end of it, just like in C++.
6

As was noted by others, std::process::exit(code: i32) is the way to go here

More information about why is given in RFC 1011: Process Exit. Discussion about the RFC is in the pull request of the RFC.

Comments

0

You can set the return value with std::os::set_exit_status.

2 Comments

This doesn't seem to be current anymore.
This question doesn't answer the "why".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.