46

I am in the process of writing a bash clone in Rust. I need to have my program exit when the user types exit. In previous iterations of my program, before I added more complicated features, I used return to get out of the loop that was prompting the user for input. This logic is now in a function, because of the way I am implementing built in shell functions, so when I return it just jumps out of the function back into the control loop, instead of short-circuiting the control loop and ending the program.

I realize that I could probably return a boolean when the user types exit and exit the loop, but I would like to at least know if Rust has a way to terminate programs early, similar to Java's System.exit(), as this is useful for certain types of programs.

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
zephyrthenoble
  • 1,037
  • 1
  • 11
  • 20
  • No, there is no safe way to terminate a Rust program early. Causing every task to unwind completely (either by returning or by failing) is the only way. – Chris Morgan Feb 06 '14 at 00:35

2 Answers2

61

Rust 1.0 stable

std::process::exit() does exactly that - it terminates the program with the specified exit code:

use std::process;

fn main() {
    for i in 0..10 {
        if i == 5 {
            process::exit(1);
        }
        println!("{}", i);
    }
}

This function causes the program to terminate immediately, without unwinding and running destructors, so it should be used sparingly.

Alternative (not recommended) solution

You can use C API directly. Add libc = "0.2" to Cargo.toml, and:

fn main() {
    for i in 0..10 {
        if i == 5 {
            unsafe { libc::exit(1); }
        }
        println!("{}", i);
    }
}

Calling C functions cannot be verified by the Rust compiler, so this requires the unsafe block. Resources used by the program will not be freed properly. This may cause problems such as hanging sockets. As far as I understand, the proper way to exit from the program is to terminate all threads somehow, then the process will exit automatically.

Kornel
  • 91,239
  • 30
  • 200
  • 278
Vladimir Matveev
  • 97,409
  • 27
  • 241
  • 264
  • I tried this and it works. It would be nice if there is a way to do this through Rust instead of through the C library, though. Thanks! – zephyrthenoble Feb 05 '14 at 15:32
  • 8
    Using `libc::exit` is a **bad idea**. It does not terminate the process cleanly and may break things dangerously. Normal Rust process conclusion *must* involve stack unwinding so that destructors are called, while `exit(3)`'s approach to concluding is quite different and does not involve stack unwinding. Thus, if you have things like open files, they may not be flushed and closed, TCP bound sockets may not be properly released (meaning you can't bind to the socket until a timeout expires, probably a minute or so), &c.—the `libc::exit` is basically equivalent to a segfault, not a clean exit. – Chris Morgan Feb 06 '14 at 00:24
  • 1
    @ChrisMorgan, yes, I was expecting something like that. But I couldn't find another way. Does Rust provide a way to terminate current task cleanly, without a fail? Or another way to stop the process? edit: oh, sorry, didn't see your comment to the question. That's a pity... – Vladimir Matveev Feb 06 '14 at 07:03
  • @ChrisMorgan Socket binding is handled by the OS; once you exit, it cleans up. You can re-bind immediately. File buffers not being flushed is indeed valid, though. – aredridel Jul 22 '14 at 03:01
  • @VladimirMatveev I get a compile error when using exit() any idea why? src\main.rs:14:27: 14:28 error: expected type, found `1` src\main.rs:14 std::process:exit(1); – twigg Jun 08 '16 at 12:58
  • @twigg there seem to be other problems in your code. This line alone cannot cause this error. – Vladimir Matveev Jun 08 '16 at 13:00
1
panic!("Oh no something bad has happened!")

Example:

    if a * g < 0f32 { panic!("The arithmetric-geometric mean is undefined for numbers less than zero!"); }

In older documentation, you will see this as fail!("Oh no something bad here has happened.")

For some reason, this macro was changed from fail to panic. Panic is the way to fail, if you must.

[edit] I am sorry. It looks like you should be testing input for the string "exit," which would depend on how you are taking input (by line or by args). Then you can have the program break out of the loop on the condition that the exit is detected.

Example:

loop {
    if exit_found { break }
    else {
        // your thing, which also looks for exit_found
    }
}
Rentsy
  • 346
  • 3
  • 12
  • 4
    Note that all `panic!` does is gracefully terminate the current task. If the function isn't currently on the main task then the `panic!` won't `exit` the program. And even if it is, it's not the same as `exit`, since `panic!` will run the destructors with arbitrary code. – ArtemGr Nov 13 '14 at 22:41
  • 1
    fail! was re-named to panic! in this RFC: https://github.com/rust-lang/rfcs/blob/091e5fabbbbd0419b8d291a6d1cd8e6e59c56deb/text/0221-panic.md – Steve Klabnik Nov 24 '14 at 00:20