Using if binding in Rust can be a little tricky

Why do I have to use "return" when I use only "if" but I don't need to use it when it is accompanied by "else"?

These codes work as they should and compile.

fn sum_if(x: u32, y: u32, cond: bool) -> u32 {
  if cond {
    x + y
  }
  else {
    0
  }
}
fn sum_if(x: u32, y: u32, cond: bool) -> u32 {
  if cond {
    return x + y
  }
  else {
    return 0
  }
}

These codes don't work as they should and compile.

fn sum_if(x: u32, y: u32, cond: bool) -> u32 {
  if cond {
    x + y;
  }
  
    0
}

These code does not compile but follows the case of the first code without "return" and the error message is not very useful.

fn sum_if(x: u32, y: u32, cond: bool) -> u32 {
  if cond {
    x + y
  }
  
    0
}
error[E0308]: mismatched types
 --> source_file.rs:6:5
  |
6 |     x + y
  |     ^^^^^ expected (), found u32
  |
  = note: expected type `()`
             found type `u32`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

Why in the last code it is not possible to use the if without return and inside and outside the if there are outputs for the function? Wouldn't that be language inconsistency?

Please don't attack me, it's just a question that hasn't left my head for a few days

You are confusing a block implicitly yielding its last expression as its value with a function returning implicitly.

if is a plain old expression. It's not magic, it does not by itself imply a return, and it does not transfer control flow to the caller function. It just yields one of the specified values based on its discriminant expression.

Where an implicit return is present is the end of a function. This is because function bodies are blocks, and in Rust, a block yields the value of its last expression. (Therefore, if the last expression of a block is a statement, then it yields (), the unit type, or "nothing".) And functions are defined to return the value of their body unless an explicit return is written. Therefore, a function implicitly returns the last value of its body block.

When you write the following:

fn sum_if(x: u32, y: u32, cond: bool) -> u32 {
  if cond {
    x + y;
  }
  
  0
}

then you have two independent expressions in the body block of the sum_if function: the first one is an if, and the second one is the literal 0. Therefore, the if expression is not the last expression of the body, and consequently, it does not get returned implicitly. The last expression in that body is an unconditional 0, so that's what gets returned regardless of what cond is. (The addition in the if is evaluated if the condition is true, but since that's a simple addition, it doesn't do much of anything.)

If you do the above and you also omit the semicolon, you get a type error because the type that an if expression yields is defined to be unit, (), if there is no else arm. This is exactly the case because if has a value, but what value could it have if there's no else but the condition happens to be false? And anyway, if you are writing an if without an else, it's likely that you are evaluating the enclosed expressions for their side effects, so the whole outer if expression yielding unit makes sense from this point of view, too.

As you can see, it's not. Just don't conflate features, and don't ask one kind of expression (if) to perform the tasks of another one (return). Expressions performing early returns implicitly would make the language practically unusable, because nothing except the first expression would ever be evaluated then.

8 Likes

Check out this answer of mine:

1 Like

Just imagine the bugs we could write if forgetting the semicolon in the middle of a function made it return early! :cold_sweat:

fn do_a_thing() {
    do_something();
    do_something_else() // oops, I forgot a semicolon here...
    do_another_thing(); // zOMG WHY DOES THIS NOT WORK?!!!?
}
4 Likes

@alice @H2CO3

I think I understand how the blocks work.

 let num = if 1 < 2 {
    1+2
  }
/// if false then num = ???, undefined value
1 Like

Right, because of this, an if without an else cannot evaluate to a value, so it wont compile if you try to leave off the last semicolon in such an if.

2 Likes

Remember that if you leave off the else, it's as though you'd written else { () }. That's why you get the type mismatch error with just (if c { 3 }).

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.