Different ways to terminate and pause program in Rust

Do not miss this exclusive book on Binary Tree Problems. Get it now for free.

Rust has multiple built-in ways to terminate the program, and every method has specific behavior whenever it's called. We're going to see how different functions in Rust (which we use to exit the program) act and how to use them.

Note that the article contains direct links to the rust-playground for you to try the code.

Contents

1. Using exit() function

std::process::exit()

This function returns nothing and terminates the program by passing the provided exit code to the OS.

And it will not call any destructors, which means you'll need to destroy everything by yourself before you exit the program using this function.

So, a better way to use exit() is to call it when you're sure that there are no destructors available.

And one way to do this is to have some different function run as a main() function, which returns an exit code or a Result<T, E> and accordingly pass the exit code to exit() function, inside your main() function.

use std::process::exit;
fn fake_main() -> i32 {
    // Let's say our program fails
    // So, we return one to the OS
    1
}

fn main() {
    let exit_code = fake_main();
    exit(exit_code);
}

Try this code

2. Using abort() function

std::process::abort()

Same as the exit() function, the abort() function returns nothing and shutdowns the program immediately after being called, which also means that as the exit() it's not going to run any remaining destructors.
Therefore this function should be used when you know that there are no destructors left to run.
Unlike the exit() function, abort() shutdowns with signal SIGABRT because of which your shell will tell you that the program has "Aborted"

Example code:

fn main() {
    println!("random");
    print!("Print that will execute but not print");
    
    std::process::abort();
    
    println!("Print that will not execute.");
}

Try this code

why 3rd line is executing but still is not printing, because by default rust refreshes stdout with every newline, and print!() does not print any newline character.

If you want 3rd line to print, then add

use std::io::{stdout, Write};

at the beginning of the code and then add

stdout().flush();

just above std::process::abort() and then it should print.

For better understanding put the \n character somewhere in the 3rd line in Example code without applying above mentioned changes.

    print!("Print\n that will execute but not print");
    //            ^---- something like this.

3. Making program panic to terminate

There are also many ways of making your program panic in Rust the simplest one is to use panic!() macro or you can also use the following ones:

  1. assert!() macro
  2. .expect() method
  3. .unwrap() method
  4. Returning Err(_) in main
  5. some macro that are wrap around panic!
    • unreachable!()
    • unimplemented!()
    • todo!()

Let's look at some of these.

3.1 panic!() macro

Unlike exit() and abort(), panic!() does run all destructors and then only terminates the program.
panic!() can also take a message that will display in stderr, and it is also capable of backtracing the error.

 fn main() {
    print!("This program only panics");
    panic!("Panicking...");
}

Try this code

Note that unlike abort() this program prints the 2nd line, that's because abort() does not empties the io Buffer.

3.2 assert!() macro

assert() takes a boolean value, So one can provide a condition that evaluates to a boolean value.
If the given condition evaluates to true the program will do nothing and will continue.
But if the given condition evaluates to false then assert will behave like the panic does.
And just like panic, assert is also capable of printing formatted messages to stderr.

use std::io::Read;
fn main() {
    println!("Input [a-z]:");
    let input = &mut [0];
    std::io::stdin().read(input).expect("fail");
    assert!(
        (input[0] as u8) >= 97 && (input[0] as u8) <= 122,
        "input = {}, is not between a-z",
        input[0]
    );
    print!("It's {}", input[0] as char);
}

Try this code

3.3 Returning Err(_) in main

By default main returns () (empty tuple) but we can also make main return Result<(), E>, and now if we return Error the program will terminate with error and will print on stderr.

Example Code:

use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
    let a = "9sa";
    a.parse::<i32>()?;
    Ok(())
}

Try this code

if a function returns Result<T, E> then ? can be used instead of the unwrap() method, and this will return Result<T, E>, instead of handling it right away.

4. Using return in the main function

But let's say you want to exit your program normally and do not require all this jargon well, then you can use return anywhere in your main function and it should exit cleanly, without leaving any mess or anything behind.

use std::io::Read;
fn main() {
    let input = &mut [0];
    std::io::stdin().read(input).expect("fail");
    if input[0] == 'a' as u8 {
        eprint!("Bad!! Bonk!!");
        return;
    }
    // All code that will not execute if the user gets bonk!! in head.
}

Seems like that rust-playground doesn't take user inputs, So, try this code on your machine, but anyway here's the code.

Pausing the program

There are also multiple ways, available to pause your program in between execution for some time or until the program detects any event from the user.

Using sleep()

One way to pause your program, for some particular time is by using sleep() which takes Duration struct as an input.

Example Code:

use std::time::Duration;
use std::thread::sleep;

fn main() {
    // you can choose between millis, nanos, micros and secs
    // we're creating time for 1 second
    let time = Duration::from_secs(1);

    // sleep
    sleep(time);
}

Try this code

This program should pause for 1 second and then terminate.

Make the program wait for an event

Any process will pause while it's waiting for an input/event from outside the program, So, we're going to make our program wait until the user presses any key.
And for that, we'll be using a crate termion that can wait and listen for an event.

Example Code:

use std::io::{stdin, stdout, Write};
use termion::input::TermRead;
use termion::raw::IntoRawMode;

fn pause() {
    let mut stdout = stdout().into_raw_mode().unwrap();
    write!(stdout, "Press any key to continue...\r\n").unwrap();
    stdout.flush().unwrap();
    stdin().keys().next();
}

fn main() {
    println!("Hello");
    // Program will pause here until any event occurs that can be register by `tty`.
    pause();
    println!("World!");
}

This code does not work on Rust-Playground.

What Rust returns when exiting?

If you have ever written a rust program before, you might know that rust returns () (empty tuple) by default for any function, even the main function returns () (empty tuple), and if you require you can also return Result<T, E> from the main function.

But what does function like exit() or panic() return when they're run well these functions return ! (never primitive type), which represents that it's returning nothing.

If we look at the function signature of the exit() function then you'll see that it's returning !

pub fn exit(code: i32) -> !

Well, ! is not something that only gets used as to exit the program, so, if you want to read more you can go to never - Rust

Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.