1

Why are implicit returns at the end allowed, but early returns require the return keyword?

fn bar() -> u64 {
    if true { 1 }
    else { 0 }
}

This is is semantically equivalent but raises an error, rust complains I should've used return 1; instead.

fn foo() -> u64 {
    if true { 1 } // early return is an error
    0
}

I can't see a technical reason why this shouldn't work, the compiler obviously is able to detect this and a (implicit) return statement is just syntactic sugar. Are there any reasons not to allow implicit early returns?


Why is the compiler asking me to add a return statement here? and the Q&A explain why the example is currently an error:

in Rust a full statement must have type () rodrigo

return x is an expression, too - it just evaluates to never and so can be used everywhere without type mismatch Cerberus

I am instead looking for an answer why this design choice was made.

Banana
  • 2,295
  • 1
  • 8
  • 25
  • 2
    Does this answer your question? [Why is the compiler asking me to add a return statement here?](/q/69642017/2189130) – kmdreko Aug 27 '23 at 16:53
  • @kmdreko OP is asking _why it was designed this way_. – Chayim Friedman Aug 27 '23 at 16:53
  • 2
    @ChayimFriedman There is some context to that in the answer, but I was asking OP specifically and not closing the question because I want to hear their perspective whether the answer there is sufficient or not. And there is further context that is linked to from there like [this Q&A](https://stackoverflow.com/questions/51049717/result-and-if-else-return-statements-e0308-error). – kmdreko Aug 27 '23 at 16:55
  • @kmdreko The question + Q&A explain why my example is currently an error in the language and how return solves it, but I would like to know why it was designed this way. – Banana Aug 27 '23 at 20:40
  • 2
    How do you expect an early return should work without a `return` statement, i.e. how should the compiler know whether an expression that is not the last one in a function should be returned, or the rest of the function should be executed? – mkrieger1 Aug 27 '23 at 20:47
  • @mkrieger1 Use the existing check that currently returns an error and let it insert the return / do the same magic like for implicit returns. I assume it scans for expressions returning something different from `()`. – Banana Aug 27 '23 at 21:35
  • Okay, I guess I understand what you mean. I think this would require special rules for `void` functions. – mkrieger1 Aug 27 '23 at 21:53
  • 2
    @Banana Generally, having my programming language make decisions about my code that run contrary to what's actually written is something that I explicitly _do not_ want. We don't need Rust to have its own version of JavaScript's automatic semicolon insertion, which can literally change code obviously intended to return an object to return undefined instead. No thank you. The fact that the Rust compiler _tells you how to fix the error_ is already miles ahead of many other popular languages. – cdhowie Aug 27 '23 at 22:59
  • Your code examples are not equivallent. in the first one the code will take one of two paths returning either 1 or 0. In the second code example the code that returns 0 will always execute, throwning away the value 1. – Branco Medeiros Aug 28 '23 at 10:02
  • Related, also possibly/probably a duplicate: https://stackoverflow.com/questions/30812469/returning-a-value-from-within-an-if-statement-has-a-mismatched-types-error (the answer here and in the target recommended above by kmdreko both explain why the language requires this. "Why" is getting into computer science weeds (because it is a logical outcome/requirement) outside the scope of Stack Overflow. Ultimately, whether you want your language to follow logical rules or not is a matter of opinion. Thus, I voted to close this as primarily opinion-based. – TylerH Aug 28 '23 at 14:42

1 Answers1

1

The problem you have presented is the result of the fact, that Rust is (primarily) an expression based language. As the Rust reference states:

Rust is primarily an expression language. This means that most forms of value-producing or effect-causing evaluation are directed by the uniform syntax category of expressions. Each kind of expression can typically nest within each other kind of expression, and rules for evaluation of expressions involve specifying both the value produced by the expression and the order in which its sub-expressions are themselves evaluated.

In contrast, statements serve mostly to contain and explicitly sequence expression evaluation.

Because of that most things that in other languages would be statements in rust are expressions instead (like for example if-else blocks).

Another, this time rather unwritten rule, is that Rust is explicit. As cdhowie pointed out in comments this is a very good thing. In the example you have presented the compiler can guess that you wanted to return early, and this will most often than not a correct guess. However even in this easy example this guess is not guaranteed to be always correct. And in some more complex example compiler conservatively assumes that it cannot be sure what the user wants.

The third argument, which is kind of an extension of the previous one, is that early return (as well as break and continue) break normal control flow of the function/loop. Therefore we must explicitly mark it as such. Otherwise it could lead to confusion about the control flow of the function.

To summarize, Rust requires early returns to be explicitly marked to:

  • be consistent with the rules about expressions
  • be conservative and do not risking guessing wrong
  • be explicit about breaking the flow of the function, which helps reader to reason about the code.
Aleksander Krauze
  • 3,115
  • 7
  • 18