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.