Question about break syntax

I am new to Rust and just started to read the book about the language. Chapter 3.3 (https://doc.rust-lang.org/book/ch03-03-how-functions-work.html) explains the difference between statements and expressions. It states that expressions do not end with a semicolon:

[...] Expressions do not include ending semicolons. If you add a semicolon to the end of an expression, you turn it into a statement, which will then not return a value.

A bit later, in chapter 3.5 (https://doc.rust-lang.org/book/ch03-05-control-flow.html), loops are discussed. The section "Returning Values from Loops" shows that the syntax:

break value;

can be used to return values from a loop. This is demonstrated by this line of code:

break counter * 2;

I am confused about the semicolon at the end of this line. According to the quote above, ending a line with a semicolon makes it a statement which does not return a value. So my question is, why does the syntax require a semicolon at this location? Did i misunderstand something?

EDIT:
I tried running the code without the semicolon at the end and it worked as well. But the question about it being there in the first place remains. Isn't that against the rule of what's a statement and what's an expression?

value or counter * 2 are expressions being "fed" to break, so as to make the whole loop { ... } block expression evaluate to it:

assert_eq!(
    42,
    loop { break 42; },
);

That being said, break $EXPR in and on itself is a(n expression-)statement, in the same way as return $EXPR is.

Thus, it can (i.e., it is optional) be followed by a semicolon so as to be able to add other statements afterwards.

loop {
    break;

    unreachable!();
}

Granted, the utility of being able to write code after an unconditionally diverging statement is quite limited, since any statement or expression is then unreachable (and thus dead) code.

On the other hand, one can also declare items within a scope, in which case the semicolon finally becomes useful:

assert_eq!(
    42,
    loop {
        break FORTY_TWO;

        const FORTY_TWO: i32 = 42;
    },
);
1 Like

Thank you for your reply! I think I have to spend more time with the language to fully understand what you wrote. It was interesting to learn that constants can be declared after they are used in Rust as long as they are in the correct scope, although I don't really see a reason why it should be done.

TIL that's even possible, don't think i've ever seen a loop returning a value in any rust code out there

Long story short,

break 42 ;

must be read as

{ break 42 };

instead of

break { 42; }

Yes I don't think this is something that's often used. But I think it's a natural consequence of most things (such as loops) being blocks that can be used as expressions.

E.g.

let result = loop { break 42; };
// result is now 42.
1 Like

looks like it can even be used together with named breaks ! :grinning:

fn main(){
    let val = 'a: loop {
        loop {
            break 'a 42;
        }
    };
    println!("{}", val);
}
2 Likes

Ah ok, thanks. This makes sense.