<![CDATA[Carrot Games]]> http://community.carrot-games.com Sun, 29 Mar 2026 04:34:07 +0000 Smartfeed extension for phpBB http://community.carrot-games.com/styles/carrotgames/theme/images/site_logo.svg <![CDATA[Carrot Games]]> http://community.carrot-games.com en-gb Sun, 29 Mar 2026 04:34:07 +0000 60 <![CDATA[Langdev :: On Rusts expression-geddon :: Author palas]]> http://community.carrot-games.com/viewtopic.php?f=21&t=26&p=51#p51 Useful terminology
(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);
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

Code: Select all

let x = match value {
  Some(v) => v,
  None => return None, 
};
wouldn't be able to be parsed if return wasn't an expression. You'd have to write

Code: Select all

=> { return None; }
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.

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; }  
    }
}
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.

Code: Select all

let x = {
  { return; };
  5
};
The type of x is in fact !.]]>
no_email@example.com (palas) http://community.carrot-games.com/viewtopic.php?f=21&t=26&p=51#p51 Wed, 11 Mar 2026 00:57:54 +0000 http://community.carrot-games.com/viewtopic.php?f=21&t=26&p=51#p51
<![CDATA[Langdev :: Re: On Rusts expression-geddon :: Reply by celes]]> http://community.carrot-games.com/viewtopic.php?f=21&t=26&p=54#p54
That last example at the end reminds me of when I had to implement this in my language. I think what you're observing here is related to the never type and how it propagates across the control flow graph of the program!

A block can either terminate (evaluate to !) or not, and whether a block terminates depends on whether the block contains a statement that terminates. This property is defined recursively so it works for nested blocks.

For control flow statements like "if" and "for", special rules also apply, like, an "if" expression terminates (evaluates to !) if all of its branches terminate. But if one of the branches doesn't, then it evaluates to the return type of the branch. When type checking an if expression, you wanna make sure all branches produce the same type, but a naive comparison would say "int ≠ never, so the if statement does not match", which is incorrect!]]>
no_email@example.com (celes) http://community.carrot-games.com/viewtopic.php?f=21&t=26&p=54#p54 Mon, 09 Mar 2026 17:19:11 +0000 http://community.carrot-games.com/viewtopic.php?f=21&t=26&p=54#p54