var b = true;
pub fn main() void {
defer {} // can't end with ;
defer if (b) {} else {}; // must end with ;
}
I think this is because the first defer is an expression, second one is a statement
I though a possible reason is the inverse. ![]()
Well expressions do not require semicolons. This makes it easy to identify what is a value and not.
_ = if (a) 1 else 0; // statement
I’m not sure. My current understanding is {} and if (b) {} else {} are the same void value.
No in the first defer you are opening a block, and blocks are expressions.
In the second one you are combining defer expression with an if expression and that turns it into a statement. At least thats how i understand it.
its extra confusing because some statements can also be expressions depending on context and vice versa.
a statement always produces a value of void or noreturn, but expressions can also produce them, so type of value is not a reliable way to tell.
the only reliable way is when the compiler tells you wether you need a semicolon or not. That being said, it is fairly intuitive in my experience.
scratch that, statements dont produce values, and therefore dont have types, any context where you can get the type from an if that if will be an expression not a statement.
it is indeed confusing, but it allows very ergonomic chaining of expressions. Sometimes cursed.
const found_a = null;
const found_b = null;
for (items) |item| if (item.foo and (found_a orelse found_b orelse false)) {
}; // statement
Looking at the language grammar:
BlockStatement
<- Statement
/ KEYWORD_defer BlockExprStatement
...
BlockExprStatement
<- BlockExpr
/ !BlockExpr AssignExpr SEMICOLON
BlockExpr <- BlockLabel? Block
Block <- LBRACE BlockStatement* RBRACE
# An assignment or a destructure whose LHS are all lvalue expressions.
AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)?
# chain of rules that eventually resolves to IfExpr (in this case)
Expr -> ...
IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix
defer {} is BlockStatement -> BlockExprStatement -> BlockExpr so no semicolon
Whiledefer if (b) {} else {}; is
BlockStatement -> BlockExprStatement -> !BlockExpr (AssignExpr -> Expr -> ... -> IfExpr) SEMICOLON which explicitly says in the grammar that defer statements that are followed by non-blocks and some kind of valid allowed expression require a semicolon afterwards, so it seems like a deliberate choice to not require the semicolon for blocks.
But my answer is basically semicolons are an explicit part of the grammar, so the grammar specifies the exact rules.
I think there is also an open issue about some other corner cases where semicolons are required and changing the rules could make it simpler or more consistent, but I don’t remember the details about that.