When to use ; in case of if expression


#1

Coming from here…
https://steveklabnik.github.io/rust-in-ten-slides/basic-syntax.html#8

if something {
    println!("it was true!");
} else {
    println!("it was false");
}

dosen’t require a ; after the }
while

let power = if too_powerful { 0 } else { 100 };

then in this case…

let mut done = false;
let mut i = 0;
while !done {
    // do stuff with i
    if i > 5 { done = true; }
}

does if i > 5 { done = true; } should end with a ; ?
like this… if i > 5 { done = true; };

Can some one explain me ?


#2

Rust’s if…else is usable both as an expression (a computation which returns a value) or as a statement (one step in your program).

As an expression, if…else can be inserted in every place where a value is expected. This includes a let statement which assigns this value to a variable/binding, as in your second example.

However, the syntax for said let statement is let <binding> = <expression>;, with a trailing semicolon. Therefore, it is the let statement, and not the if…else, which mandates the use of a semicolon in your second example.

When if…else is used in isolation, as a statement, no semicolon is needed.


#3

It’s probably easier to say that if is always acts as an expression in Rust, as such it always returns a value. When a block ends without a ; it just means an implicit return, but when it ends with a ; it means that the “last expression” is return (). It’s possible to say let empty: () = if true { println!("OK"); } else { println!("NOK"); } . An if branch can contain either a block ending with or without ; as long as both branch returns the same type.

if true { () } else { println!("O-Oh"); } // good
if true { () } else { println!("O-Oh") } // good, as println!() returns ()
if true { () } else { 1; } // good, note the ; after 1

#4

To clarify the seeming dissonance between the last two comments:

Rust’s syntax mostly unifies the concepts of statements and expressions. It allows many constructs in expressions that other languages don’t, such as {} blocks, return, break, etc. In fact, you can almost imagine that Rust has no such thing as statements at all, only expressions: think of ; as a binary operator like && or ||, which throws away the left-hand side and yields the right-hand side (or () if there’s nothing after the ;). That’s why the last line of a function can return a value implicitly, without having to write return – but only if you don’t end the line with ;, which would throw away the value.

…But this is only mostly true! In reality, statements and expressions are separate things, just like in other languages, although in Rust there are only a few differences between them. One of them is that let (and some other things) can only appear as a statement: you can’t write (let y = 5;), although {let y = 5;} is okay (because the inside of a {} block is treated as containing statements, even if the block itself is in the middle of an expression). Another is that ; can be optionally omitted when an if statement (or similar construct) is followed by another statement – the behavior is the same as if it were there – which doesn’t apply when if appears as an expression.


#5

That ; might be a bit confusing to someone new to Rust. The 1 in the example just disappears. The semi-colon terminates the statement, resulting in () being returned by the if..else expression.


#6

Thanks everyone for the explanation ! I guess I got it clear, just to iterate in the context of the example. In the while loop the ; is not needed because, we are not assign the result of the if condition to a variable using let and even if we add ; it not going to effect because its not the return statement.