2

Why does the following code raise an error when compiled?

fn test(n: i32) -> Result<i32, &'static str> {
    if n == 0 {
        Err("error")
    }
    Ok(n + 1)
}

Following is the error:

error[E0308]: mismatched types
  --> src/main.rs:41:9
   |
41 |         Err("error")
   |         ^^^^^^^^^^^^- help: try adding a semicolon: `;`
   |         |
   |         expected (), found enum `std::result::Result`
   |
   = note: expected type `()`
              found type `std::result::Result<_, &str>`

The following two versions compile without any problem.

  1. With else statement:

    fn test(n: i32) -> Result<i32, &'static str> {
        if n == 0 {
            Err("error")
        }
        else {
            Ok(n + 1)
        }
    }
    
  2. With return statement:

    fn test(n: i32) -> Result<i32, &'static str> {
        if n == 0 {
            return Err("error");
        }
        Ok(n + 1)
    }
    

I'm using Rust 1.27.0.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Nick
  • 10,309
  • 21
  • 97
  • 201
  • 1
    I believe your question is answered by the answers of [Why does an if without an else always result in () as the value?](https://stackoverflow.com/q/43335193/155423) and [Why does the compiler assume that the value of if let should be `()`?](https://stackoverflow.com/q/37646475/155423) and [return a value from within an if statement in a match statement](https://stackoverflow.com/q/30812469/155423). If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Jun 26 '18 at 19:04

1 Answers1

5

Imagine this simplified code:

fn test() -> i32{
    { 1 }
    2
}

This fails with the following error:

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

That is because in Rust a full statement must have type (). If you want to ignore a value, just adding a ; converts a value into a statement, and changes the type to () discarding the value.

This code compiles:

fn test() -> i32{
    { 1; }
    2
}

Your example is similar, but the if makes things more interesting. If you write:

fn test(c: bool) -> i32{
    if c { 1 }
    2
}

It will fail, just as before, because the first statement has a type different from (). Adding a ; solves the issue:

fn test(c: bool) -> i32{
    if c { 1; }
    2
}

Writing an else also compiles, because then there is only one statement in the function and its type matches the return type of the function:

fn test(c: bool) -> i32{
    if c { 1 }
    else { 2 }
}

Note that both branches must have the same type, and no ; in either.

Adding a return will also work, because a return statement has () by definition, so any of these will compile:

fn test1(c: bool) -> i32{
    if c { return 1; }
    2
}
fn test2(c: bool) -> i32{
    if c { return 1 }
    2
}
fn test3(c: bool) -> i32{
    if c { return 1; }
    else { 2 }
}
fn test4(c: bool) -> i32{
    if c { return 1; }
    else { return 2; }
}

Note how the ; is actually optional in these return statements, because it is already of type ().

rodrigo
  • 94,151
  • 12
  • 143
  • 190