Diverging Functions - functions that never return

exit panic! ! never exclamation mark error code

If we don't say what a function returns, it is expected to return ():

fn say_hi(name: &str) {
    println!("Hello {}", name);
}

However, there are function that never return. For example because they call the panic! macro or the std::process::exit function.

For these the recommendation is to add an exclamation mark ! as the designated return type. This is also called the never type.

fn say_hi_an_panic(name: &str) -> ! {
    println!("Hello {}", name);
    panic!("Oh my");
}

A real-world use-case

When would we need to use this?

This is a simplified version of the code I used in the resize image with error handling example.

We are expecting a number on the command line that can be represented using a 32-bit unsigned integer. We would like to provide a usage if the user did not supply anything or if the value could not converted to a u32.

examples/invalid-parameter/src/main.rs

fn main() {
    let number = get_args();
    dbg!(number);
}

fn get_args() -> u32 {
    let args = std::env::args().collect::<Vec<String>>();

    if args.len() != 2 {
        eprintln!("Usage: {} INTEGER", &args[0]);
        std::process::exit(1);
    }

    let number: u32 = match args[1].parse() {
        Ok(value) => value,
        Err(err) => {
            eprintln!("Invalid parameter: '{}'. It must be an integer", err);
            eprintln!("Usage: {} INTEGER", &args[0]);
            std::process::exit(1);
        }
    };

    number
}

Here we have a match that is expected to return a 32-bit unsigned integer, a u32. However, if the user supplied an invalid argument and we cannot parse and convert the input to a u32 we would like to print some error message, a usage statement showing how the user should run the program and then we would like to exit.

    let number: u32 = match args[1].parse() {
        Ok(value) => value,
        Err(err) => {
            eprintln!("Invalid parameter: '{}'. It must be an integer", err);
            eprintln!("Usage: {} INTEGER", &argv[0]);
            std::process::exit(1);
        }
    };

This code works well and even clippy is happy.

However, we have some duplicated code:

eprintln!("Usage: {} INTEGER", &args[0]);
std::process::exit(1);

What happens if we factor this out to a separate function?

The code with diverging function

examples/invalid-parameter-usage/src/main.rs

fn main() {
    let number = get_args();
    dbg!(number);
}

fn get_args() -> u32 {
    let args = std::env::args().collect::<Vec<String>>();

    if args.len() != 2 {
        usage(&args[0]);
    }

    let number: u32 = match args[1].parse() {
        Ok(value) => value,
        Err(err) => {
            eprintln!("Invalid parameter: '{}'. It must be an integer", err);
            usage(&args[0]);
        }
    };

    number
}

fn usage(name: &str) -> ! {
    eprintln!("Usage: {} INTEGER", name);
    std::process::exit(1);
}

Here we need the exclamation mark on the usage function.

If we declare the usage function without any return value:

fn usage(name: &str) {
    eprintln!("Usage: {} INTEGER", name);
    std::process::exit(1);
}

We will get an error:

error[E0308]: mismatched types
  --> src/main.rs:14:21
   |
14 |           Err(err) => {
   |  _____________________^
15 | |             eprintln!("Invalid parameter: '{}'. It must be an integer", err);
16 | |             usage(&args[0]);
17 | |         }
   | |_________^ expected `u32`, found `()`

For more information about this error, try `rustc --explain E0308`.

Unfortunately the explanation of E0308 does not describe this case and does not offer the solution that we needed here.

Luckily the Rust users forum already had an answer: Function calling other that exits, and branch type mismatch and the Rust by Example had an example for Diverging functions.

In type theory this idea is called bottom type.

Conclusion

As I spent quite some time till I found this solution I though I should explain it. It helps me remember and maybe it will help a few other people avoid this problem or solve it faster.

Related Pages

Resize image using Rust
Functions in Rust
Exclamation mark in Rust !
Compiler Error codes in Rust

Author

Gabor Szabo (szabgab)

Gabor Szabo, the author of the Rust Maven web site maintains several Open source projects in Rust and while he still feels he has tons of new things to learn about Rust he already offers training courses in Rust and still teaches Python, Perl, git, GitHub, GitLab, CI, and testing.

Gabor Szabo