e.g. `let x = match y { ... };`
Yet everyone here is making a distinction between blocks and not-blocks. Is "my rule" better?
Furthermore, if you say the semicolon is for the let I think it misleads people. Eg if you do `match foo { .. }`, with no let and no semicolon (which is what that rule would suggest), you're actually returning the value of the match. Same goes for a non-match block, too. Eg, `{ .. }` will return the contents. So if you want to prevent that, you use a semicolon. `{ .. };`.
Am I missing something? Why are so many people describing it with the block vs semicolon thing?
This is a common misconception, but it isn't true.
Rust is an expression-oriented language. This means that most things are expressions, and evaluate to some kind of value. However, there are also statements. "Item declarations" (stuff like defining structs, etc) and let statements are the two most common kinds of statements. However, there's also an "expression statement", where you can take any expression, add ; to it, and it becomes a statement rather than an expression.
A block has the following grammar:
BlockExpression :
{
InnerAttribute*
Statements?
}
Statements :
Statement+
| Statement+ ExpressionWithoutBlock
| ExpressionWithoutBlock
A block is made of (leaving some things out for simplicity) zero or more statements, where statements is either one or more statements, one or more statements and an expression, or an expression.Note that blocks are themselves expressions, and so evaluate to a value. The only way that you get "no semicolon for return" is when the expression is in this tail position, when its value is the value of the whole block.
... does that make sense? Perusing https://doc.rust-lang.org/reference/statements-and-expressio... might help.
I feel like what you said is what I said, but with more accuracy and depth. Eg, if my block was:
{
foo();
bar()
baz();
}
My statement holds true, no? It's obviously a syntax problem, since I'm trying to define a statement after I define an expression (which would be trying to execute code after a return, effectively).So.. I'm not arguing (I trust you to be right lol), but I struggle to understand which part of my statement is inaccurate, beyond the simplifications/etc at least.
To word it differently; I still feel what I said..
> If it's an expression, use a semicolon otherwise you're returning it
seems more accurate as a mental cheatsheet than:
> no semicolons after things that use {}, semicolons after everything else.
Since {}'s can so often result in a value returning, defaulting to not using semicolons just because it's a {} seems odd. I would think focusing on if it's an expression would be easier to understand, no?
Appreciate your time as always Steve :)
To be honest, my mental model of all of this is "write code, fix it when the compiler complains"; I rarely think about semicolons, and cargo fmt will remove extraneous ones, so it's sometimes hard for me to go anywhere between "these are the rules of the grammar" and "don't think about it at all". My failing, not yours :)
If the last statement/expression in a block is an expression not followed by ;, the block evaluates to that expression. Otherwise (such as, the last expression has a ;), the block evaluates to ().
The function "return" behaviour is because function bodies are blocks: a function returns the value of its block (or any `return`).
Putting a semicolon after an expression makes it into a statement, which are unit-valued, which is why the expression's value gets suppressed. It's a subset of "statements not ending with blocks" (since an expression-statement doesn't take a statement-block even if the expression itself has one).
> no semicolons after things that use {}, semicolons after everything else.
Which seems fundamentally flawed for a cheatsheet. Unless you never intend to return anything with a bracket, I guess.