Search code examples
functionrustreturn-type

Understanding Rust's function return type


I am trying to get into Rust by learning it from its book. Mostly common things with interesting additions on it. I try to code some stuff to get used to it so I decided to do as the chapter says and write a quick Fibonacci function but had an error I am having problems wrapping my head around. Any rust expert who would explain to me why is this happening and the logic behind it.

fn fibo(x: i32) -> i32 {
    if x == 0 {
        0
    }
    else if x == 1 {
        1
    }

    fibo(x-1) + fibo(x-2)    
}

When I try to build this code, I get the following error;

error[E0308]: mismatched types
 --> src/main.rs:6:9
  |
6 |         0
  |         ^ expected (), found integer
  |
  = note: expected type `()`
             found type `{integer}`

error[E0308]: mismatched types
 --> src/main.rs:9:9
  |
9 |         1
  |         ^ expected (), found integer
  |
  = note: expected type `()`
             found type `{integer}`

But If I change the code to the following it works fine;

fn fibo(x: i32) -> i32 {
    if x == 0 {
        0
    }
    else if x == 1 {
        1
    }else{
        fibo(x-1) + fibo(x-2)    
    }
}

In the Rust Language book, it is stated that the compiler checks the type of all the expressions in the if-else blocks but also states that it passes to the next line of code if there is no else statement is present. Since I stated the return type would be i32 how come the compiler can expect type "()" as seen in the error?


Solution

  • The problem here is that you're trying to have two "return" statements.

    When a block's last statement lacks a semicolon, the result of the statement is the type of evaluating the entire block. In pseudo-rust, we can say the following

    {
        0
    } -> usize
    

    That's to say

    let x = { 0 };
    

    Makes a new scope for the 0, implicitly returns it because there's no semicolon in the block, and then that becomes the type of the block. So, x: usize.

    So, what's going on?
    Your code has two implicit returns:

    fn fibo(x: i32) -> i32 {
        if x == 0 { // This starts a block
            0 // First implicit return
        }
        else if x == 1 { // This also starts a block
            1 // First implicit return
        } // Therefore this entire if/else statement is an implicit return unless its output
          // is suppressed with a semicolon.
    
        fibo(x-1) + fibo(x-2) // Here we try to return again, which confuses rust!
    } 
    

    Because your if/else contains implicit returns and is not being assigned to a variable, it is like as if I said the following:

    fn fibo(x: i32) -> i32 {
        //For the sake of explanation, let's assume x == 0
        0 // instead of your if/else
    
        fibo(x-1) + fibo(x-2)
    }
    

    Oh no! How to fix this:

    fn fibo(x: i32) -> i32 {
        if x == 0 {
            // return 0; // This is an early return; this will break until it reaches the function
            0 // Since this is about implicit returns I'll use them here
        } else if x == 1 {
            1
        } else {
            fibo(x - 1) + fibo(x - 2)
        }
    }