If expressions and semicolons


#1

Hi,

I have a question about semicolons.

if true {} else {} is an expression, not a statement.

But this code can be compiled successfully:

fn main() {
    if true {} else {}
    if true {} else {}
    if true {} else {}
}

I think that I need semicolons after if expressions because:

In practical terms, Rust’s grammar expects statements to follow other statements. This means that you use semicolons to separate expressions from each other.

https://doc.rust-lang.org/book/functions.html#expressions-vs-statements

Why does this code work?

Thanks,


#2

Some expressions can be statements without requiring a semicolon. Here is the relevant code from rustc.

/// Does this expression require a semicolon to be treated
/// as a statement? The negation of this: 'can this expression
/// be used as a statement without a semicolon' -- is used
/// as an early-bail-out in the parser so that, for instance,
///     if true {...} else {...}
///      |x| 5
/// isn't parsed as (if true {...} else {...} | x) | 5
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
    match e.node {
        ast::ExprKind::If(..) |
        ast::ExprKind::IfLet(..) |
        ast::ExprKind::Match(..) |
        ast::ExprKind::Block(_) |
        ast::ExprKind::While(..) |
        ast::ExprKind::WhileLet(..) |
        ast::ExprKind::Loop(..) |
        ast::ExprKind::ForLoop(..) => false,
        _ => true,
    }
}

#3

Which, because of the ExprKind::Block above, also means this is a valid function (https://is.gd/hWmIFx)

fn main() {
    { () }
    { () }
    { () }
}

One other thing that is checked is that the result of a statement must be (), so this (https://is.gd/ZFuR9P):

fn main() {
    { true }
    { () }
}

fails with

2 |     { true }
  |       ^^^^ expected (), found bool

similarly an if and match parsed as a statement must have all its arms resulting in () to be valid (commonly by putting a ; at the end of each arm).


#4

@dtolnay @Nemo157 Thanks!
I understand it.