Factorial if statement expect '()' found integer

Viewed 143

I'm new to rust (coming from programming in c/c++ and python) so to learn I'm writing some basic functions. Below I have a factorial function that takes in a signed integer and has two if checks for it.

fn factorial(x: i32) -> i32 {
    let result = if x > 1 {
        x * factorial(x-1)
    } else if x <= 1 {
        1
    };

    result
}

To my knowledge, the if and else-if blocks should handle every case for it. However, when compiling it throws the following error:

error[E0317]: `if` may be missing an `else` clause
  --> src/main.rs:22:12
   |
22 |       } else if x <= 1 {
   |  ____________^
23 | |         1
   | |         - found here
24 | |     };
   | |_____^ expected `()`, found integer
   |
   = note: `if` expressions without `else` evaluate to `()`
   = help: consider adding an `else` block that evaluates to the expected type

error: aborting due to previous error

For more information about this error, try `rustc --explain E0317`.
error: could not compile `functions`

If I replace the else-if with just an else, it compiles just fine. Why do I need to replace it with an else? Shouldn't the previous else-if be good enough?

1 Answers

As the error message says, if an if expression doesn't have an else then the type of the expression is (). This is because the expression can only have one type, and there is no sensible default value in the general case for if the condition evaluated to false - that's what else is for!

In your case, the compiler could have figured out that the two predicates are in fact exhaustive. It doesn't, and that really is just how it is. If the compiler could detect exhaustiveness in this case it would be weird if it couldn't also detect it in other "obvious" cases. But predicates can be arbitrary expressions and it would be impossible to implement the check in the general case.

In this example, the compiler would have to analyse the body of the random function to know if the predicates are exhaustive or not:

// random(x) might return a different value each time it's called
if random(x) > 1 {
    1
} else if random(x) <= 1 {
    2
} // Not exhaustive!

Being consistent seems like the best choice for the language, since you can always add an else at the end.

Related