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".
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
.
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.
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 here9 | | };| |_____^ 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).
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.
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.
pub fn bigger(a: i32, b: i32) -> i32 {if a > b {return a;}b}
This post was co-authored by Prince Wilson