I understand the concepts of expressions and statements in Rust, but one piece of code in the book "The Rust Programming Language" confused me.
Here is the code:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
result is assigned an expression (otherwise the code wouldn't work) but the semicolon after counter * 2 made me think that this is an statement.
The authors write elsewhere that
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
Can someone clarify this for me please?
Rust is an expression-oriented language. This means that most constructs including control-flow constructs are expressions. An expression followed by a semicolon ; is a statement, whose effect is to evaluate the expression and discard its result. Therefore, the distinction between expression and statement is less important. However, some expressions diverge and that's a little weird if you're coming from other languages.
break counter * 2 is an expression. What is the type and value of this expression? It is a diverging expression. It has no value, and the type is a type ! that has no values. Imagine writing:
let foo = break counter * 2;
Ask what is the type of foo. One cannot have the effect of break, which is essentially a goto, while also returning a value and continuing the loop. The type of a break expression therefore is always a type without values. An uninhabited type, a type without any values, may seem odd but conceptually it's fine. It's the return type of functions that never return. It's the type of infinite loops, which can never evaluate to a value.
Yes, break counter * 2; is a statement which discards the value of the expression break counter * 2, but the value of the expression is not counter * 2.
What is the type of loop { ... }? By fiat, it is the type of the expressions within any break expressions in the loop. The value of the loop must come from one of the break expressions.
So, if you add some types:
fn main() {
// The type of counter is i32, because it is not
// suffixed with a different literal type, and no
// use below causes a different type to be inferred.
let mut counter: i32 = 0;
// The type of result is the type of the loop
// expression, which by definition is the type of
// the expressions passed to `break` within the
// loop. There is only one `break`, which is passed
// counter * 2, of type i32.
let result: i32 = loop {
counter += 1;
if counter == 10 {
// The type of this expression is !.
// Since a semicolon follows, the value
// is discarded, but the expression has no value.
break counter * 2;
}
};
println!("The result is {}", result);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With