I was reading through the Rust documentation and came across the following example and statement
Using a return as the last line of a function works, but is considered poor style:
fn foo(x: i32) -> i32 {
if x < 5 { return x; }
return x + 1;
}
I know I could have written the above as
fn foo(x: i32) -> i32 {
if x < 5 { return x; }
x + 1
}
but I am more tempted to write the former, as that is more intuitive. I do understand that the function return value should be used as an expression so the later works but then why wouldn't the former be encouraged?
It just is.
Conventions don’t need to have particularly good reasons, they just need to be generally accepted conventions. As it happens, this one does have a comparatively good reason—it’s shorter as you don’t have the return
and ;
. You may think that return x + 1;
is more intuitive, but I disagree strongly—it really grates and I feel a compelling need to fix it. I say this as one who, before starting using Rust, had never used an expression-oriented language before. While writing Python, return x + 1
in that place looks right, while writing Rust it looks wrong.
Now as it happens, that code should probably be written thus instead:
fn foo(x: i32) -> i32 {
if x < 5 {
x
} else {
x + 1
}
}
This emphasises the expression orientation of the language.
Copied from reddit: Why isn't the syntax of return statements explicit?
Answer from @pcwalton
Explicit return is really annoying in closures.
For example,
it was a major pain in JavaScript before ES6 arrow functions were introduced
myArray.map(function(x) { return x * 2; })
is gratuitously verbose, even without the function
keyword.
Once you have implicit returns somewhere in your language,
you might as well have them everywhere for consistency's sake.
The fact that it makes code less verbose is just an added bonus.
and from @mozilla_kmc
Rust is an expression-oriented language.
A block has the form
{
stmt;
stmt;
...
stmt;
expr
}
The statements are (basically) expressions or let
bindings,
and the trailing expression is implicitly ()
if not specified.
The value of the whole block is the value of this last expression.
This is not just for functions. You can write
let foo = if x { y } else { z };
so if
also takes the place of C's ?:
operator.
Every kind of block works the same way:
let result = unsafe {
let y = mem::transmute(x);
y.frob()
};
So the implicit return at the end of a function is a natural consequence
of Rust's expression-oriented syntax.
The improved ergonomics are just a nice bonus :)
Puzzle: return x
itself is an expression -- what is its value?
Answer:
It is unit type ()
.
The clippy lint gives the following rational for the needless_return
lint:
Removing the return and semicolon will make the code more rusty.
This is probably as good as an objective rationale as we will ever get.
As far as intuition goes; I feel that it is shaped by our individual experiences and therefore subjective. While Rust is not a functional programming language itself, many people using and developing it seem to have a strong background in functional programming languages like Haskell, which are entirely based on expressions. The influence can be strongly felt in many areas of Rust (e.g. error handling). So for them (and to be honest, myself included) using an expression instead of a statement seems more elegant.