62

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jeroen
  • 15,257
  • 12
  • 59
  • 102
  • 4
    See [this recent post on reddit too](http://www.reddit.com/r/rust/comments/284y7n/why_cant_main_return_an_int/). – huon Jun 16 '14 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. – Chris Morgan Jun 16 '14 at 13:57
  • @ChrisMorgan Yeah I was expecting that. Didn't think the buffers would be a problem though. – Jeroen Jun 16 '14 at 13:58
  • 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). – Chris Morgan Jun 16 '14 at 14:00
  • @ChrisMorgan I'm pretty sure you're wrong about that. See your local man page for `exit(3)`. `_exit(2)` and `_Exit(2)` are the ones that don't perform cleanup such as flushing buffers. – Mike Dec 13 '15 at 05:53
  • 1
    @Mike: that’s *system* buffers. Where buffering is done in user space (e.g. `BufWriter`), `exit` will not flush the buffers. – Chris Morgan Dec 17 '15 at 00:59
  • 1
    Also this RFC discussion: https://github.com/rust-lang/rfcs/issues/1176. – ArtemGr Jan 05 '16 at 21:10

6 Answers6

70

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>> {
   ...
}
9769953
  • 10,344
  • 3
  • 26
  • 37
  • 3
    @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 Jun 03 '18 at 13:55
  • @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. – Jeroen Dec 03 '18 at 23:35
30

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.

timlyo
  • 2,086
  • 1
  • 23
  • 35
  • 5
    *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? – Shepmaster Nov 15 '16 at 13:57
  • I'm not entirely sure, I was going off of [this post](https://www.reddit.com/r/rust/comments/284y7n/why_cant_main_return_an_int/ci7gg1h/?st=ivjkhtc6&sh=4e8c92ec) and just updating this hoping to start a discussion about it while I researched a bit more. – timlyo Nov 15 '16 at 14:03
  • 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. – derekdreery Dec 26 '16 at 14:49
  • 3
    Also note that this will immediately exit the process, will not rewind the stack thus will not gracefully shutdown. `Drop`s will not be called. – Eray Erdin Feb 14 '22 at 12:08
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.

Melle
  • 7,639
  • 1
  • 30
  • 31
5

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.

Community
  • 1
  • 1
Jack O'Connor
  • 10,068
  • 4
  • 48
  • 53
  • 1
    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. – Ruslan May 06 '16 at 15:46
  • 3
    From C99 the `main` function behaves as if there was a `return 0;` statement at the end of it, just like in C++. – krzaq May 28 '17 at 00:45
2

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)
}
Wojtas
  • 21
  • 2
-1

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

A.B.
  • 15,364
  • 3
  • 61
  • 64