I'm gonna be honest, I never liked the idiomatic way of writing return values:
fn foo() -> u32 {
42
}
I've tried the past few months to get used to it, but unsuccessfully so. I do it anyway because it's idiomatic. I think one thing that makes it so hard for me to swallow is that it feels inconsistent to me. I expect to be able to end every scope the same way, but this -- for instance -- doesn't work:
I thought the rule was simply that omitting return and ';' is only allowed at the very bottom of a function. But this turns out not to be entirely true either; I've randomly tried to remove them here and there and in some inner conditional scopes it works and most others it doesn't.
I'm thinking that I may learn to live with it if I understood what the rules are. So .. what are they?
The rule is that a block evaluates to an expression if the final expression in the block evaluates to one (and has no semicolon).[1] This is true not just for functions, but any block:
let vec = {
let mut buf = vec![];
buf.push(3);
buf
};
If you have a function like
fn boole(b: bool) -> i32 {
if b {
1
} else {
0
}
}
that works because if...else is an expression. (as is if let...else and match). Maybe you might even like to format it as
fn boole(b: bool) -> i32 {
if b { 1 } else { 0 }
}
To be pedantic, all blocks evaluate to an expression, but those with a trailing semicolon evaluate to (). ↩︎