On Rusts expression-geddon
Posted: Mon Mar 09, 2026 6:23 am
Useful terminology
(this one is for yoshi-enthusiast):
I've been wondering for a while what's the reasoning behind rust's parser aggressive "make everything an expression" attitude. There are only f̶o̶u̶r̶ three types of statements: item, macro, let binding and expression with ; at the end.
So why make return, break, etc. expressions? They all evaluate to ! anyway. and furthermore it results in weirdness like
being valid rust.
Today it suddenly hit me. (UPD: and as it happens, right after the "discovery" I've stumbled on a HN discussion where the exact same thing is stated multiple times).
I'm sure there are more, but one of the benefits is improved ergonomics of match statements. Code like
wouldn't be able to be parsed if return wasn't an expression. You'd have to write
Though sometimes you still have to wrap the expression in {}, like when it's a function call which returns something, while match evaluates to ().
I was doing to ponder on how this could be avoided, but it really isn't a problem in the first place. Having a block clearly indicates the intent to ignore the output of a function, which would otherwise be either lost completely (potentially bad), or needed to be stated in a comment (gross).
Bonus: enjoy this beautiful piece (and a valid rust program), proudly residing at weird_exprs.rs, among other specimen.
There's also a quirk with return I wanna discuss. You might think that's it's just an expression with a type ! and additional effect of terminating the function it's contained in. But that would not be correct! It (along with anything else which returns ! ) has an additional ability to change the types of the outer expressions, however nested.
The type of x is in fact !.
(this one is for yoshi-enthusiast):
- ! (pronounced as "never") is a special type, which can never be instantiated. It has an important property of being auto-convertable to any other type. This is useful for language primitives that affect control flow. For example:
Code: Select all
//this function never exits fn panic() -> ! { ... }Code: Select all
let opt = Some(10); let value: i32 = match opt { Some(v) => v, None => panic!("it crashed"); // ! is converted to i32, as in this branch we never return anyway };
I've been wondering for a while what's the reasoning behind rust's parser aggressive "make everything an expression" attitude. There are only f̶o̶u̶r̶ three types of statements: item, macro, let binding and expression with ; at the end.
So why make return, break, etc. expressions? They all evaluate to ! anyway. and furthermore it results in weirdness like
Code: Select all
let x = foo(return 5);
Today it suddenly hit me. (UPD: and as it happens, right after the "discovery" I've stumbled on a HN discussion where the exact same thing is stated multiple times).
I'm sure there are more, but one of the benefits is improved ergonomics of match statements. Code like
Code: Select all
let x = match value {
Some(v) => v,
None => return None,
};
Code: Select all
=> { return None; }
I was doing to ponder on how this could be avoided, but it really isn't a problem in the first place. Having a block clearly indicates the intent to ignore the output of a function, which would otherwise be either lost completely (potentially bad), or needed to be stated in a comment (gross).
Bonus: enjoy this beautiful piece (and a valid rust program), proudly residing at weird_exprs.rs, among other specimen.
Code: Select all
fn zombiejesus() {
loop {
while (return) {
if (return) {
match (return) {
1 => {
if (return) {
return
} else {
return
}
}
_ => { return }
};
} else if (return) {
return;
}
}
if (return) { break; }
}
}
Code: Select all
let x = {
{ return; };
5
};