70 %

Why can't I early return in an if statement in Rust?

The following Rust code contains a bigger function that takes two numbers as arguments and returns a number. It also contains what might look like an if statement that guards one return value, otherwise returning b. However it doesn't compile like we might expect in other languages, even with Rust's "implicit returns".

rust
pub fn bigger(a: i32, b: i32) -> i32 {
if a > b {
a
}
b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ten_is_bigger_than_eight() {
assert_eq!(10, bigger(10, 8));
}
#[test]
fn fortytwo_is_bigger_than_thirtytwo() {
assert_eq!(42, bigger(32, 42));
}
}

The error we see is expected () found i32.

rust
error[E0308]: mismatched types
--> src/main.rs:8:9
|
7 | / if a > b {
8 | | a
| | ^ expected `()`, found `i32`
9 | | }
| | -- help: consider using a semicolon here
| |_____|
| expected this to be `()`

The reason for this is core to Rust. Rust doesn't have "implicit returns" because (almost) everything in Rust is an expression which has a return value. The exception to this are statements of which there are only a few. One statement is ;, which takes an expression, throws away the expression's value, and evaluate to () instead. So let's use a semicolon on our if statement.

rust
pub fn bigger(a: i32, b: i32) -> i32 {
if a > b {
a
};
b
}

This yields additional information in our error that points vaguely at the underlying issue: if may be missing an else clause.

error[E0317]: `if` may be missing an `else` clause
--> src/main.rs:7:5
|
7 | / if a > b {
8 | | a
| | - found here
9 | | };
| |_____^ expected `()`, found `i32`
|
= note: `if` expressions without `else` evaluate to `()`
= help: consider adding an `else` block that evaluates to the expected type

The reason for this is if is an expression, and thus has a return value. because if has a return value, the type of the return value from both branches has to match. By not writing an else branch, we've declared the return value from the non-existent else branch to be (), which doesn't match with the return type of the if branch, which is i32 because that is the type of a.

We can prove this by moving b into the else branch of our if expression and printing the return value out. (note that we've used 0 as a temporary i32 return value for our function while we print to keep everything running).

rust
pub fn bigger(a: i32, b: i32) -> i32 {
println!("the value is {}", if a > b { a } else { b });
0
}

which given a call such as bigger(42, 20) would print out the following.

the value is 42

So in the end because if expressions are indeed expressions we have to pull b into our expression to take advantage of the return value from the if expression as the return value of our bigger function.

rust
pub fn bigger(a: i32, b: i32) -> i32 {
if a > b {
a
} else {
b
}
}

Finally note that you can explicitly return, which acts as you might expect in other languages.

rust
pub fn bigger(a: i32, b: i32) -> i32 {
if a > b {
return a;
}
b
}

This post was co-authored by Prince Wilson