Rust Diverging Functions
A diverging function is a function that never hands control back. Rust writes its return type as !, the never type.
Write -> ! for never returns
A function that never hands control back ends in a panic, an endless loop, or a process exit. The arrow in its signature says so:
fn fail(message: &str) -> ! {
panic!("{message}")
}! fits any type
A call to fail never produces a value. So the compiler lets it stand anywhere. It can stand wherever a value is owed. Here, a &str is owed in one match arm:
let text = String::from("warm");
let label: &str = match text.as_str() {
"warm" => "t-shirt weather",
_ => fail("unknown weather"),
};
println!("label = {label}");continue is a ! expression
continue never produces a value either. That is why it can sit in a match arm that owes a u32:
let mut total = 0;
for i in 0..6 {
let addition: u32 = match i % 2 == 1 { true => i, false => continue };
total += addition;
}
println!("total = {total}");The unit type is the opposite
() — the unit type — has exactly one value, also written (). ! sits at the other extreme: it has no values at all.
let nothing: () = ();
println!("unit = {nothing:?}");exit is typed ! too
std::process::exit stops the program, so it never returns. Its type is a function pointer ending in !:
let code: fn(i32) -> ! = std::process::exit;
// exit stops the program, so control never comes backExample
fn main() {
fn sum_odd_numbers(up_to: u32) -> u32 {
let mut acc = 0;
for i in 0..up_to {
let addition: u32 = match i % 2 == 1 {
true => i,
false => continue,
};
acc += addition;
}
acc
}
println!("sum of odds below 10 = {}", sum_odd_numbers(10));
let value: u32 = match Some(7) {
Some(n) => n,
None => panic!("no value"),
};
println!("value = {value}");
}Key points
- Declaring your own -> ! function is rare: you meet ! far more often inside the expressions above than in your own signatures
In this example, sum_odd_numbers adds the odd numbers below 10. Inside the match, the true arm gives i, a u32. The false arm gives continue — which never produces a value at all, so it is type !, and ! fits the u32 the match must produce. The total prints as 25. Below, the second match gives 7 from its Some arm, while its None arm calls panic!, another ! expression, so value is a plain u32 and prints as 7. Neither diverging arm runs in this program — but both had to type-check, and ! is what lets them.
Note
Code after a diverging call still type-checks
The compiler treats it as unreachable
It does not treat it as wrong
It knows control never arrives there
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Why can continue sit in a match arm that must produce a u32?