_______               __                   _______
       |   |   |.---.-..----.|  |--..-----..----. |    |  |.-----..--.--.--..-----.
       |       ||  _  ||  __||    < |  -__||   _| |       ||  -__||  |  |  ||__ --|
       |___|___||___._||____||__|__||_____||__|   |__|____||_____||________||_____|
                                                             on Gopher (inofficial)
   URI Visit Hacker News on the Web
       
       
       COMMENT PAGE FOR:
   URI   Weird Expressions in Rust
       
       
        nfrmatk wrote 2 hours 40 min ago:
        There's a fun RustConf talk on this topic from a few years ago.
        
   URI  [1]: https://youtu.be/tNVDjMxCz-c
       
        bensons1 wrote 3 hours 42 min ago:
        I am sort of amused, a memory safe language deploys this sort of
        juggling
       
        sureglymop wrote 3 hours 55 min ago:
        With yesterdays release let chains got released. Really needed feature
        but can sometimes look pretty cursed:
        
   URI  [1]: https://blog.rust-lang.org/2025/06/26/Rust-1.88.0/#let-chains
       
        keybored wrote 4 hours 23 min ago:
        Once I tried how many `{{{...}}}` I needed to make javac crash. It
        wasn’t that many.
       
        lacker wrote 4 hours 49 min ago:
        I think there's a mistake in the explanation for "bathroom_stall". When
        describing the guard in this expression:
        
          if (i+=1) != (i+=1)
        
        The post says, "The if statement is always going to be false because
        the right expression will always be one more than the left." But it's a
        not-equals. The if statement is always going to be false because in
        Rust "i += 1" doesn't return an integer value, it returns a (). So
        comparing any two += statements, they are always equal. Since the guard
        is a != comparison, the if statement is always false.
       
        xg15 wrote 5 hours 9 min ago:
        Rust noob here.
        
        That '!' type seemed weird in the first few examples but starts to make
        sense later on.
        
        It's essentially a "pseudo type" for everything that is syntactically
        an expression, but will never return anything, because evaluating it
        causes the entire statement to be canceled.
        
        Is that correct?
       
          int_19h wrote 3 hours 7 min ago:
          Not necessarily the entire statement, just some outer expression.
          
          Which might make more sense when you remember that the only
          statements in Rust are various declarations (`let`, `type`, `fn` etc)
          and macro invocations. Everything else is an "expression statement",
          including blocks and loops. Thus you can do stuff like:
          
              // Compute the first Fibbonaci number >10
              let n = {
              let mut x1 = 0;
              let mut x2 = 1;
              loop {
                  let x = x1 + x2;
                  if x > 10 { break x }
                  x1 = x2;
                  x2 = x;
              }
              };
          
          Note that `break` never leaves the let-statement here - it just
          terminates the loop expression and forces it to yield a value
          (`break` without arguments yields (), and ditto for loops without
          break).
          
          You can also break out of regular blocks if they are labelled and you
          use the labelled form of break:
          
             let x = 'label: { ... break 'label 42 ... }
          
          This all can very easily lead to convoluted code if not used
          sparingly, but sometimes a mutating loop with mutable data
          encapsulated within and a break to yield it once the computation is
          complete is genuinely the most straightforward way to write
          something.
       
          NobodyNada wrote 4 hours 30 min ago:
          Yes, exactly -- it's called the Never type.
          
          It's also useful in more places than return expressions -- for
          example, you can make a function return ! to indicate that it's a
          non-returning function, which is useful for expressing, say, an error
          handler that must crash the program; or a main loop that must never
          return. It also can help the compiler generate more compact code when
          a function is known to not return.
          
          There's currently work in progress to allow you to specify ! as a
          type everywhere, not just as function returns. This is useful where
          some generic code expects a function to return a Result with an
          implementation-specified error type, since an infallible
          implementation can specify ! as the error type. Then, the type
          checker can allow the programmer to unwrap a Result without checking
          for errors, and the optimizer can remove the error-checking branches
          from generic code: [1] This has taken a very long time to implement,
          because of some very subtle implications on type inference that made
          it difficult to stabilize without breaking compatibility -- but the
          2024 edition finally figured out a way to make it possible.
          
   URI    [1]: https://doc.rust-lang.org/std/primitive.never.html
       
          Analemma_ wrote 5 hours 1 min ago:
          Yes. If you look at steveklabnik's example with the match statement
          elsewhere in the comments, it makes sense that '!' is the "never" or
          "unreachable" type, not because the return expression isn't run, but
          because its value will never be assigned to a variable, since it
          causes an unconditional exit from the function.
       
        JoeOfTexas wrote 5 hours 18 min ago:
        Bruh, I started learning Rust yesterday.  Why do you do this to me. 
        Now I don't know anything I studied.
       
          steveklabnik wrote 5 hours 13 min ago:
          You don't need to know any of this. It's just a parsing stress test,
          with meaningless programs. It's fun trivia.
       
        munificent wrote 5 hours 28 min ago:
        Does anyone know why `union` isn't a reserved word in Rust?
        
        Most contextual keywords in other languages come from either:
        
        1. Features that were added after the language was in wide use and
        can't add keywords without breaking existing code.
        
        2. Features where the word is particularly useful elsewhere, so would
        be painful to reserve (like `get` and `set` in Dart).
        
        But neither of those seem to apply to Rust. As far as I know, it's
        always had ML-style unions, and "union" doesn't seem to be a
        particularly useful identifier otherwise.
        
        Why isn't `union` fully reserved?
       
          pitaj wrote 5 hours 13 min ago:
          It's a common operation on sets, so would make `HashSet::union` [1]
          and friends less obvious, for no real benefit.
          
   URI    [1]: https://doc.rust-lang.org/stable/std/collections/struct.Hash...
       
          steveklabnik wrote 5 hours 26 min ago:
          It's simply that Rust has higher standards for breaking changes than
          "probably not in wide use." In other words, that someone could have
          had `let union =`... somewhere was a reason to make it contextual.
          
   URI    [1]: https://rust-lang.github.io/rfcs/1444-union.html#contextual-...
       
            munificent wrote 22 min ago:
            Ooooooh, I see my confusion now.
            
            My brain switched off and I got enums and unions confused. I was
            like, wait, hasn't Rust had them since day one? I was thinking of
            `enum`, not `union`. My bad.
       
        pluto_modadic wrote 5 hours 33 min ago:
        I've absolutely seen some /cursed/ rust one liners.
        
        if you extend it to the most cursed ~6 lines of code, you really can
        obfuscate what you're doing in a way that's fiendishly hard to debug.
       
        kzrdude wrote 6 hours 3 min ago:
        Many of them are on the same theme - the theme is `return -> !`.
        
        Here's my favourite on that theme, which I was missing from the list:
        
            return return return return return return return return return 1
       
          wredcoll wrote 4 hours 44 min ago:
          Is there some meanining to the number of returns?
       
            kzrdude wrote 4 hours 9 min ago:
            No, it's just nonsense. Same result with fewer or more returns..
       
        arjvik wrote 6 hours 21 min ago:
        I figured out how return-as-a-value made sense only upon realizing that
        in the following code,
        
            fn funny(){
            fn f(_x: ()){}
            f(return);
            }
        
        f() is never called because funny() returns before it gets called.
        
        The reason you want return to be coercible to any type is so that you
        can write something like
        
            let x: i32 = if y {
            4
            } else {
            return; // type ! coerced into i32
            }
        
        And you pick the return value of ! because return never actually
        produces a value that is propagated on at runtime, it immediately exits
        the function.
        
        (Note this all works even with returning a value)
       
        Sniffnoy wrote 6 hours 45 min ago:
        I don't understand the one with dont().  Why does i.get() end up false
        at the end?  Shouldn't it be true after having been set by dont()?
       
          LegionMammal978 wrote 6 hours 2 min ago:
          The final line "assert!(i.get());" asserts that i.get() is true in
          the end. The ! character here belongs to the assert! macro, not a
          boolean negation. (This unfortunately gets a bit weird-looking when
          you want to write sensible stuff like "if !matches!(x, Some(5..=10))
          { ... }".)
       
            Sniffnoy wrote 3 hours 38 min ago:
            Oops, I see, thanks!
       
        armchairhacker wrote 7 hours 20 min ago:
        Related: [1] Rust programs that give unintuitive outputs or compile
        errors.
        
   URI  [1]: https://dtolnay.github.io/rust-quiz
       
        npalli wrote 7 hours 48 min ago:
        yeah this is good, but now add lifetime annotations to have fun.
       
          steveklabnik wrote 7 hours 45 min ago:
          Lifetimes are not expressions and therefore can't really be put into
          odd places.
       
        Thaxll wrote 7 hours 53 min ago:
        Why assigning a variable to a function that returns nothing is not a
        compilation error?
       
          dathinab wrote 6 hours 4 min ago:
          assuming you mean returning () (the empty tuple/void type)
          
          because it doesn't compose well with generics, macros, proc-macros
          etc.
          
          e.g. if you have this dump way to wrap clone: `fn foo(v: T) -> (T, T)
          { let new = v.clone(); (v, new) }` it would implicitly not work with
          T = (), because then `v.clone()` would be a "function which returns
          nothing".
          
          In isolation this might seem fine but if you compose abstractions you
          sooner or later run into an edge case where it isn't fine.
          
          And this becomes even more a problem when macros/proc-macros are
          involved.
          
          It also makes changing code easier, lets say you have something like
          `let x = foo(); dbg!(x);` and you change the return type it will keep
          compiling as long as the type implements `Debug`, even if you change
          the type to `()` (i.e. return nothing). And again for normal code
          that is a minor nit pick, but for macros, proc macros, generic code
          of sufficient complexity sooner or later you run into edge cases
          where it really matters that it's allowed. Not often but often
          enough.
          
          Lastly and most importantly assigning `()` to a variable hurts no
          one, you won't see any such code in normal PRs.
          
          So it it doesn't hurt anyone but can be quite use full in edge cases.
          
          Lastly linters (mainly clippy) do warn or error for some of this
          nonsensical things, depending on the lint-set you enabled.
       
          tialaramex wrote 6 hours 31 min ago:
          Do you mean a function which returns "nothing" in the sense that it
          does return but it has no particular value to return, like Vec::clear
          which gets rid of the values but preserves the capacity of the
          container ?
          
          In Rust the return type of this function is the unit type, the empty
          tuple (). So, the variable has this type, there's no problem with
          this in Rust, even though some lesser languages can't handle the idea
          of a type this small.
          
          Or did you mean a function which never returns, like
          std::process::exit ? In Rust this function's return type is ! the
          Never type, an empty type that you ordinarily can't name in stable
          Rust.
          
          Because this type is empty, a variable of this type will evaporate,
          the compiler knows that we can't bring values into existence if there
          are no values of that type, the code paths in which this variable
          exists will never be executed, so no need to emit machine code.
          
          In a language with generic programming like Rust this isn't an error,
          it's actually a convenience. We can write generic error handling
          code, and then for cases where there will never be an error our error
          handling code doesn't even compile, it evaporates entirely, yet for
          cases which can have actual errors, the error handling code is
          emitted.
       
          assbuttbuttass wrote 7 hours 39 min ago:
          There's no such thing as a "function that returns nothing" in Rust.
          Unit, written as (), is a first class value and can be assigned to a
          variable, although there's not much point
       
          steveklabnik wrote 7 hours 44 min ago:
          Which example are you referencing?
       
        IshKebab wrote 8 hours 2 min ago:
        Honestly I'm surprised how not weird these are. Way less WTFy than
        Javascript, PHP, C or C++.
       
          dathinab wrote 6 hours 1 min ago:
          yes but to be fair the blog is focused on unusual aspects of
          everything being an expression
          
          you still can create some more confusing things by idk. overloading
          some operators (but luckily not `=` and similar crazy C++ things)
          adding recursive macros and maybe combining it with lifetime variance
          and coercion edge cases, maybe sprinkle in some arcane `#[]`
          annotations and people with be very confused, more so then in the
          article
       
            IshKebab wrote 5 hours 28 min ago:
            Yeah... I'm just saying I haven't seen anything close to these
            things: [1]
            
   URI      [1]: https://github.com/denysdovhan/wtfjs
   URI      [2]: https://github.com/satwikkansal/wtfpython
       
        nikolayasdf123 wrote 8 hours 46 min ago:
        this is why I like Go
       
          nemo1618 wrote 7 hours 48 min ago:
          I wonder, what's the "weirdest" expression in Go? Here's one:
          
             type Foo struct{}
             func (Foo) Bar() { println("weird...") }
             func main() {
              ([...]func(){^^len(`
             
             
             `): (&Foo{}).Bar})[cap(append([]any(nil),1,2,3))]()
             }
       
            assbuttbuttass wrote 7 hours 35 min ago:
            Makes sense to me, [...]func() is an array of functions, and
            [...]T{index: value} is uncommon but still perfectly comprehensible
       
              jenadine wrote 6 hours 58 min ago:
              That's why I like Rust /s
       
              nemo1618 wrote 7 hours 16 min ago:
              Many people aren't aware that you can use key: val declarations
              in arrays
       
          timeon wrote 8 hours 5 min ago:
          It does not have stress tests for parser?
       
        steveklabnik wrote 8 hours 54 min ago:
        This post is missing my favorite one!
        
            fn evil_lincoln() { let _evil = println!("lincoln"); }
        
        What's weird about this?
        
        To understand what evil_lincoln is doing, you have to understand very
        old Rust. Here's the commit that introduced it: [1] fn evil_lincoln() {
            let evil <- log "lincoln";
            }
        
        log was a keyword to print stuff to the screen. Hence the joke, [2] Now
        that log is the println! macro, the joke is lost.
        
        It doesn't say explicitly why this is "weird", but given some other
        comments in the file,
        
            // FIXME: Doesn't compile
            //let _x = log true == (ret 0);
        
        I am assuming that using the return value of log was buggy, and so this
        tested that you could save it in a variable. I don't remember the exact
        semantics of log, but if it's like println!, it returns (), which is
        useless, so binding it to a variable is something you'd never write in
        real code, so it's "weird" in that sense.
        
   URI  [1]: https://github.com/rust-lang/rust/commit/664b0ad3fcead4fe4d22c...
   URI  [2]: https://en.wikipedia.org/wiki/Lincoln_Logs
       
          mbStavola wrote 3 hours 44 min ago:
          A dog entered a tavern and said: "I cannot see anything, I'll open
          this one!"
       
            hansjorg wrote 2 hours 37 min ago:
            Tough crowd.
       
          rendaw wrote 7 hours 49 min ago:
          What's the joke exactly? English is my native language.
       
            steveklabnik wrote 7 hours 46 min ago:
            log "lincoln" is a reference to the toy "lincoln logs"
       
          ibotty wrote 8 hours 19 min ago:
          What's the joke exactly?  English is not my native language.
       
            jerf wrote 8 hours 10 min ago:
             [1] This would be something the Boomer generation grew up with,
            and I think maybe the previous generation too. They're still around
            but they've certainly faded; they used to be Lego-level popular
            kids toys back then. They are named after President Lincoln, but
            only as a marketing tactic to use some of his reputation, there's
            no real connection.
            
            I would imagine even some native English speakers are learning
            something with this post. I haven't seen them in a while.
            
   URI      [1]: https://www.basicfun.com/lincoln-logs/
       
              saghm wrote 5 hours 55 min ago:
              I'm a late millennial, and I'd sometimes see them as a kid too.
              I'm not sure about more recent generations, but I think that they
              might have stuck around longer than you might think.
       
                iforgotpassword wrote 4 hours 55 min ago:
                I saw some kids in a park a few years ago in Beijing playing
                with those. First time I saw them. Didn't know the name until
                now though. :)
       
              bennettnate5 wrote 6 hours 37 min ago:
              > They were named after President Lincoln, but only as a
              marketing tactic
              
              > there's no real connection
              
              Funny--I always thought it was meant to be a pun on linkin', as
              in you're linkin' the logs together because they have those slots
              that fit precisely together on the ends.
       
                saghm wrote 5 hours 53 min ago:
                I think it's both that and the popular tale of Lincoln having
                been born in a log cabin (which for some reason I thought I had
                heard wasn't actually true, but from looking into it now, it
                seems like a lot of sources say it is, so maybe I heard wrong?)
       
              drfuchs wrote 7 hours 39 min ago:
              There’s a strong connection between President Lincoln and log
              cabins.  He grew up in a series of log cabins, and this fact was
              widely known during his campaign.
       
              ranguna wrote 8 hours 5 min ago:
              Yes, but why is it evil?
       
                Analemma_ wrote 7 hours 28 min ago:
                I think that part is a reference to a Futurama episode where a
                holodeck malfunction materialized several villains, including
                "Evil Lincoln".
       
        b0a04gl wrote 8 hours 55 min ago:
        they exist because whole language built to treat expressions as
        firstclass citizens : blocks, ifs, matches, even macros as expressions
        that return values. so once you internalize that, all these weirdo one
        liners are artifacts. just artifact of a system where expressions
        compose infinitely.  the syntax tree runs deeper than most people's
        habbits allow. you hit that depth and brain says this is wrong but
        compiler's allowing.
       
          AIPedant wrote 6 hours 22 min ago:
          Hmm my read is this is a slight overstatement - Rust was always built
          with the idea of expressions as first class citizens, but
          practicality and performance requires expression-breaking keywords
          like “return” which don’t fit neatly in an ML-ish language and
          have a few plain old hacks associated with implementing them (not
          “hack” as in lacking robustness; I mean theoretically/formally
          inelegant). Likewise there’s some stuff (u8) which is a simple
          syntax quirk. But a lot of these return/etc oddities are because Rust
          is ultimately an imperative language with strong influence from
          functional programming.
       
            GrantMoyer wrote 4 hours 5 min ago:
            Haskell has `bottom`[1] (see also [2]), which acts like Rust's
            `return` from a type checking perspective.
            
            I wouldn't call using a uninhabited type for the type of a return
            expression theoretically inelegant. On the contrary, I find it
            quite pleasing.
            
            [1]
            
   URI      [1]: https://wiki.haskell.org/Bottom
   URI      [2]: https://en.wikipedia.org/wiki/Bottom_type
       
              dmkolobov wrote 32 min ago:
              On the more mainstream side of things, Typescript also has a
              bottom type called `never` which is used to type
              unreachable/exceptional code.
       
            efnx wrote 5 hours 30 min ago:
            I’ve found rust to be an ML in C’s clothing.
            
            The main difference from other MLs is the lack of higher kinded
            types, so it’s difficult to express things like Functor, Monad,
            Arrow, etc
       
            steveklabnik wrote 5 hours 58 min ago:
            return is an expression in Rust, and it fits in well.
            
            There are very few statements: [1] and a lot of expressions:
            
   URI      [1]: https://doc.rust-lang.org/stable/reference/statements.html
   URI      [2]: https://doc.rust-lang.org/stable/reference/expressions.htm...
       
              missinglugnut wrote 4 hours 21 min ago:
              Steve, I know you're an authority on the language but you've
              dismissed the point being made here without engaging with it.
              
              Return is a statement in the minds of most programmers, but an
              expression in the language. That was a very pragmatic decision
              that required an unintuitive implementation. As a result, we've
              got this post full of code that is valid to the compiler but
              doesn't make a lick of sense to most programmers reading it.
       
                int_19h wrote 4 hours 1 min ago:
                We've been going down this road for a long time now. E.g.
                "throw" is a (void-typed) expression in C++ already for similar
                reasons, although it doesn't go far enough without a proper
                bottom type. C# took it further and added the type so that you
                can write things like e.g. `x = y ?? throw new Error(...)`.
                There's no obvious reason why "return" should be conceptually
                different.
                
                A better question at this point, arguably, is why there should
                even be an expression/statement distinction in the first place.
                All imperative statements can be reasonably and sensibly
                represented as expressions that produce either () or "never".
                Semicolon then is just a sequencing operator, like comma in
                C++.
       
                steveklabnik wrote 4 hours 15 min ago:
                > Return is a statement in the minds of most programmers
                
                I would take issue with this, sure, for a lot of people, they
                may be bringing assumptions over from languages where
                assignment is a statement. That doesn't make them correct.
                
                > required an unintuitive implementation
                
                To some people, sure. To others, it is not unintuitive. It's
                very regular, and people who get used to "everything is an
                expression" languages tend to prefer it, I've found.
       
                  hansvm wrote 3 hours 4 min ago:
                  > people who get used to "everything is an expression"
                  languages tend to prefer it, I've found
                  
                  I.e., if we bias our sample to the data points proving our
                  point then our point is proven. It's like that quip about how
                  every car insurance company can simultaneously claim "people
                  who switched saved hundreds of dollars in average."
                  
                  I also like "everything is an expression" languages, but I
                  don't think that's a fantastic argument.
       
              AIPedant wrote 4 hours 45 min ago:
              We're speaking past each other since there's "expression" as
              defined in the Rust specification vs "expression" as in ordinary
              computer science, and Rust's use of return is certainly not an
              expression in the latter sense.  It is shoehorned into being
              called an expression but it has no semantically meaningful type,
              it is an effect. A type is (carefully but somewhat arbitrarily)
              assigned to it, which is why some of those examples involving
              "return" are particularly goofy. It is not material for most
              programs since it only comes up with intentional misuse of the
              keyword. But "return" does not make sense in functional languages
              with true first-class expressions - functions don't return
              values, they get evaluated and the frame destruction / etc are
              all abstracted away. It makes sense in Rust because expressions
              in the CS sense of the term are ultimately not first class.
       
                octachron wrote 3 hours 9 min ago:
                Return (or other effects) does make sense as an expression in a
                functional language. Typically, OCaml has `raise Exception`
                which is also an expression, with the same type as `return` or
                any never returning function. And exceptions can also be used
                to implement a user-defined `return` function.
       
                int_19h wrote 4 hours 9 min ago:
                Pure functional languages have the equivalent of "never" - it's
                the bottom type. Indeed, the return type of `error` in Haskell
                is that, but also cases like predictable infinite recursion.
                But this semantics works great for cases like "return" and
                other forms of control transfer - the expression in which they
                appear also "never finishes" (but some other expression which
                contains that one as a subexpression does).
                
                Now, yes, ideally you'd have effects in the type system so that
                you can express this kind of stuff with more precision. But if
                you restrict this to stuff like return/break/continue where the
                destination is statically known and can be validated, you can
                treat those effect types as been there, just inferred for all
                expressions and forbidden to cross the function boundary.
                
                For exceptions specifically this trick no longer works because
                the whole point is for them to cross that boundary. But the
                amount of complexity this stuff adds to typing even trivial
                generic code is arguably too much for practical use (see also:
                checked exceptions in Java). In any case, in Rust you use
                Result types instead so those exceptions produce regular
                values. And although panics can be handled, they are certainly
                not meant to be used as a generic mechanism for transfer of
                control, so adding effect types for them alone is just not
                worth it.
       
                steveklabnik wrote 4 hours 17 min ago:
                I do think we're speaking past each other. I don't fully agree
                with your "CS sense of the term," as Rust does have a
                semantically meaningful type: !. This is all pretty
                bog-standard stuff. Rust isn't doing anything weird or novel
                here.
       
          throwawaymaths wrote 7 hours 7 min ago:
          its not just that some things you would usually think are control
          flow are expressions, its also that there are unusual rules around
          coercing the `noreturn` type.
       
          derriz wrote 7 hours 8 min ago:
          That sounds superficially reasonable to me and I'm all for regularity
          in programming language semantics but on thinking about it further, I
          actually think it's a design flaw.
          
          It makes no more sense to me for "return " to have a type than it
          does to make "if " or "break" or "{" or any other keyword to have a
          type.  These are syntactic elements.
          
          Rust's type system is clearly inspired by Hindley-Milner and most
          languages using such a type system either don't even have a return
          keyword.
          
          Even if you disagree with this argument, this design decision has
          resulted in all these weird/confusing but absolutely useless code
          examples and there is no upside that I can see to this decision in
          terms of language ergonomics.  What practical value is it to users to
          allow "return " to itself be an expression?  That you can use such an
          "expression" as arguments to function calls with hilarious wtf
          consequences?  It's a piece of syntactic sugar.
       
            pornel wrote 6 hours 22 min ago:
            This makes the language more uniform. Instead of having a special
            ternary ?: operator for if-else in expression position, you have
            one syntax everywhere.
            
            It makes generic code work without need to add exceptions for
            "syntactic elements". You can have methods like `map(callback)`
            that take a generic `fn() -> T` and pass through `T`. This can work
            uniformly for functions that do return values as well as for
            functions that just have `return;`. Having nothingness as a real
            type makes it just work using one set of rules for types, rather
            than having rules for real types plus exceptions for "syntactic
            elements".
       
            dathinab wrote 6 hours 36 min ago:
            it doesn't need to make sense on a higher abstraction level of
            logic/semantics
            
            I mean you don't see any of the nonsense in the blog post in any
            realistic PR (so they don't matter),
            
            but you would run into subtle edge case issues if some expressions
            where more special then other expressions (so that does matter),
            
            especially in context of macros/proc macros or partial
            "in-progress" code changes (which is also why `use` allows some
            "strange" {-brace usage or why a lot of things allow optional
            trailing `,` all of that makes auto code gen simpler).
       
            deathanatos wrote 6 hours 42 min ago:
            We can use match to do pattern matching:
            
              let name = match color_code {
                0 => "red",
                1 => "blue",
                2 => "green",
                _ => "unknown",
              };
            
            The RHS of the `=>` has to be an expression, since we're assigning
            it to a variable. Here, you should already see one "useful"
            side-effect of what you're calling "syntactic elements" (I'd
            perhaps call them "block statements", which I think is closer to
            the spirit of what you're saying.) The whole `match … {}` in the
            example above here is an expression (we assign the evaluation of it
            to a variable).
            
            > What practical value is it to users to allow "return " to itself
            be an expression?
            
            Now, what if I need to return an error?
            
              let name = match color_code {
                0 => "red",
                1 => "blue",
                2 => "green",
                _ => return Err("unknown color"),
              };
            
            The expression arms need to be the same type (or what is the type
            of `name`?). So now the type of the last branch is !. (Which as you
            hopefully learned from TFA, coerces to any type, here, to &str.)
            
            There's more ways this "block statements are actually expressions"
            is useful. The need not be a ternary operator / keyword (like C,
            C++, Python, JS, etc.):
            
              let x = if cond { a } else { b };
            
            In fact, if you're familiar with JavaScript, there I want this
            pattern, but it is not to be had:
            
              const x;  // but x's value will depend on a computation:
              // This is illegal.
              if(foo) {
                x = 3;
              } else {
                x = 4;
              }
              // It's doable, but ugly:
              const x = (function() { if(foo) { return 3; } else { return 4;
            }})();
              // (Yes, you can do this example with a ternary.
              // Imagine the if branches are a bit more complicated than a
            ternary,
              // e.g., like 2 statements.)
              
            
            Similarly, loops can return a value, and that's a useful pattern
            sometimes:
            
              let x = loop {
                // e.g., find a value in a datastructure. Compute something.
            Etc.
                if all_done {
                  break result;
                }
              };
            
            And blocks:
            
              let x = {
                // compute x; intermediate variables are properly scoped
                // & cleaned up at block close.
                //
                // There's also a slight visual benefit of "here we compute x"
            is
                // pretty clearly denoted.
              };
            
            > Even if you disagree with this argument, this design decision has
            resulted in all these weird/confusing but absolutely useless code
            examples
            
            I think one can cook up weird code examples in any language.
       
              wredcoll wrote 4 hours 48 min ago:
              I appreciate, so much, that rust is slowly evolving into perl.
       
            bobbylarrybobby wrote 6 hours 48 min ago:
            The issue with `return expr` not having a type is that you lose the
            ability to write something like
            
            let y = match option {
                Some(x) => x,
                None => return Err("whoops!"),
            };
            
            Without a type, the None branch loses the ability to unify with the
            Some branch. Now you could say that Rust should just only require
            branches’ types to unify when all of them have a type, but the !
            never type accomplishes that goal just fine.
       
              derriz wrote 5 hours 36 min ago:
              I'm responding here because so many replies are making the same
              point.
              
              In your particular example, let's put your example into a
              context.  Is
              
                fn foo(option: Option) -> i32 {
                   let y = match option { Some(x) => x, None => return
              Err("whoops!"), };
                   return 1;
                }
              
              well typed?  It should be if we are to believe that "return " is
              an expression of type () - but, naturally, it causes a
              compilation error because the compiler specifically treats
              "return " unlike other expressions.  So there is no improvement
              in regularity, while it admits all sorts of incomprehensible
              "puzzlers".
              
              I don't see why you'd lose this ability if you removed the claim
              that "return " is itself an expression.  Most/many languages have
              mechanisms to allow expressions to affect flow control - e.g.
              with exceptions, yield, etc. - which do not these constructs (for
              example "throw x") to have a type.
              
              Rust could just as easily supported the syntax you use above
              without making "return " a tapeable expression.
       
                bobbylarrybobby wrote 5 hours 26 min ago:
                The type of return is !, not (). Meaning there are zero
                instances of this type (whereas there is one instance of ()). !
                can coerce to any type.
                
                Also, the type of return is a separate matter from the type of
                the thing being returned. You obviously can't return Result
                from a function returning i32. The point of type coercion is
                that you can yield `return Err(...)` in one branch of a match
                and have it type check with the other branch(es).
       
                efnx wrote 5 hours 27 min ago:
                Well now we’re just talking about personal preferences, then.
       
                steveklabnik wrote 5 hours 29 min ago:
                > Is ... well typed?
                
                It's not, but not due to the return, it's because you're trying
                to return a Result from a function that returns an i32. This
                works:
                
                  fn foo(option: Option) -> Result {
                     let y = match option { Some(x) => x, None => return
                Err("whoops!"), };
                     return Ok(1);
                  }
                
                > It should be if we are to believe that "return " is an
                expression of type ()
                
                It is not, it is an expression of type !. This type unifies
                with every other type, so the overall type of y is i32. return
                is not treated in a special way.
                
                >  if you removed the claim that "return " is itself an
                expression
                
                This code would no longer work, because blocks that end in an
                expression evaluate to (), and so you would get the divergent,
                not well typed error, because one arm is i32 and the other is
                ().
       
                  derriz wrote 5 hours 18 min ago:
                  Sorry for the confusion - I meant to use ! and not ().
                  
                  "It's not, but not due to the return, it's because you're
                  trying to return a Result from a function that returns an
                  i32."
                  
                  That's exactly my point.  "return " is not just an expression
                  which can be typed.  If you tell me the types of all the
                  identifiers used, I can look at any  expression in Rust which
                  does not include a return, and tell you if it's well typed or
                  not.  If the expression includes a return, then I cannot tell
                  you whether the expression is well-formed.
       
                    steveklabnik wrote 5 hours 15 min ago:
                    > "return " is not just an expression which can be typed.
                    
                    Yes, it is, and it can. It has the type !, no matter the
                    type of .
       
                      derriz wrote 4 hours 22 min ago:
                      It only has type !, if the return type of the lexically
                      enclosing function declaration has the same type as that
                      of , otherwise it's illformed.
                      
                      For any expression NOT involving "return", I can write,
                      for example:
                      
                      const Z = 
                      
                      but I cannot if  contains a return embedded somewhere. 
                      The existence of a "return" somewhere in an expression
                      changes the character of the entire expression.
                      
                      I.e. there are two classes of "expressions".  Those NOT
                      containing returns (which are equivalent to the notion of
                      "expression" in the languages that Rust was inspired by)
                      and those containing a return somewhere in them which are
                      subject to further rules about wellformedness.
                      
                      My point is that none of this is necessary at all - you
                      don't need to provide type rules for every lexical
                      feature of your language to have a language with a
                      powerful expressive type system (like Rust's).
       
                        int_19h wrote 3 hours 37 min ago:
                        You can write it just fine if `const Z` is itself
                        nested inside a function definition.
                        
                        And this isn't really any different from variable
                        references, if you think about it. If you have an
                        expression (x + 1), you can only use it somewhere where
                        there's an `x` in scope. Similarly, you can only use
                        `return` somewhere where there's a function to return
                        from in scope. Indeed, you could even make this
                        explicit when designing the language! A function
                        definition already introduces implicit let-definitions
                        for all arguments in the body. Imagine if we redefined
                        it such that it also introduces "return" as a local,
                        i.e. given:
                        
                           fn foo(x: i32, y: i32) -> i32 {
                             ...
                           }
                        
                        the body of the function is written as if it had these
                        lines prepended:
                        
                           let x = ...;
                           let y = ...;
                           let return = ...;
                           ...
                        
                        where "return" is a function that does the same thing
                        as the statement. And similarly for break/continue and
                        loops.
                        
                        The thing that actually makes these different from real
                        variables is that they cannot be passed around as
                        first-class values (e.g. having the function pass its
                        "return" to another function that it calls). Although
                        this could in fact be done, and with Rust lifetime
                        annotations it would even be statically verifiable.
       
                        steveklabnik wrote 3 hours 59 min ago:
                        Okay, I think we are indeed talking past each other and
                        I see what you are saying here. I am not sure that I
                        agree, exactly, but I appreciate your point. I'm going
                        to have to think about it a bit more.
       
            NobodyNada wrote 6 hours 52 min ago:
            In practice, it's quite a useful feature, because you can write
            things like this:
            
                let day_number = match name {
                "Sunday" => 0,
                "Monday" => 1,
                "Tuesday" => 2,
                "Wednesday" => 3,
                "Thursday" => 4,
                "Friday" => 5,
                "Saturday" => 6,
                _ => return Err("invalid day")
                };
       
            steveklabnik wrote 6 hours 56 min ago:
            Respectfully, "it makes no sense to me" isn't an argument. if and
            break both have types in Rust as well.
            
            > don't even have a return keyword.
            
            This is because they are not procedural languages, it has nothing
            to do with the type system.
            
            >  there is no upside that I can see to this decision in terms of
            language ergonomics.
            
            There's tremendous upside! That's why lots of languages choose
            this. For example, there is no need for the ternary in Rust: if can
            just do that.
            
            >  What practical value is it to users to allow "return " to itself
            be an expression?
            
            Code like this just works:
            
                let guess: u32 = match guess.trim().parse() {
                    Ok(num) => num,
                    Err(_) => return,
                };
            
            That is, if return wasn't an expression, we'd have a type error:
            the two arms would have incompatible types.
       
              derriz wrote 5 hours 29 min ago:
              See my comment above, your example only "just works" if the
              enclosing function has the appropriate return type (in this case
              none).
              
              So the syntactic element "return" is not just an expression -
              unlike other sub-expressions, it involves action at a distance -
              i.e. it must not just agree with it's context as part of an
              expression but it must agree with the enclosing fn signature.
       
                steveklabnik wrote 5 hours 25 min ago:
                I replied over there, let's keep it to that sub-tree so we both
                don't have to duplicate comments :)
       
          gmueckl wrote 7 hours 35 min ago:
          If a language that claims to be security focused is easily able to
          express constructs that human minds find barely comprehensible, or
          worse, then this is itself arguably a security issue: it's impossible
          to check the correctness of logic that is incomprehensible.
       
            int_19h wrote 3 hours 33 min ago:
            "never" is an easily comprehensible concept once you start asking
            the right questions.
            
            But also, all examples in TFA are very artificial convoluted code.
            Meaning that you can write things like these just like you can
            write something like &&&...x - but why would you? Actual real-world
            uses of this feature are all quite readable.
       
            pornel wrote 6 hours 36 min ago:
            What's the threat model? If you're reviewing untrusted or
            security-critical code and it's incomprehensible, for any reason,
            then it's a reject.
            
            Syntax alone can't stop sufficiently determined fools. Lisp has
            famously simple syntax, but can easily be written in an
            incomprehensible way. Assembly languages have very restrictive
            syntax, but that doesn't make them easy to comprehend.
            
            Rust already has a pretty strong type system and tons of lints that
            stop more bad programs than many other languages.
       
              gmueckl wrote 5 hours 56 min ago:
              Many modern language designers focus on shaping expressibility
              rather than providing the maximum possible flexibility because
              their designers learned from C, Lisp and other languages that
              made mistakes. Examples lamguages are Java, C#, D, Go... some
              arguably with more success than others. But language design that
              gave ultimate expressive power to the the programmer is a relic
              of the past.
       
                pornel wrote 3 hours 33 min ago:
                ???
                
                "Expressibility" and "expressive power" are vague and
                subjective, so it's not clear what you mean.
                
                I suppose you object to orthogonality in the syntax? Golang and
                Java definitely lack it.
                
                But you also mention C in the context of "maximum possible
                flexibility"? There's barely any in there. I can only agree it
                has mistakes for others to learn from.
                
                There's hardly any commonality between the languages you list.
                C# keeps adding clever syntax sugar, while Go officially gave
                up on removing its noisiest boilerplate.
                
                D has fun stuff like UFCS, template metaprogramming, string
                mixins, lambdas — enough to create "incomprehensible" code if
                you wanted to.
                
                You're talking about modern languages vs relics of the past,
                but all the languages you mention are older than Rust.
       
                  gmueckl wrote 1 hour 38 min ago:
                  Have you ever seen submissions to IOCCC or Underhanded C Code
                  Contest? That is what too much syntactic flexibility looks
                  like (if taken to the extreme).
                  
                  If you want your code to be secure, you need it to be
                  correct. And in order for it to be correct, it needs to be
                  comprehensible first. And that requires syntax and semantics
                  devoid of weird surprises.
       
            steveklabnik wrote 7 hours 31 min ago:
            > If a language that claims to be security focused
            
            Rust does not claim to be particularly security-focused, only
            memory safe.
            
            Also, this means that you'd consider any expression-based language
            to be inherently a security problem.
       
              gmueckl wrote 7 hours 10 min ago:
              "Memory safety" is an aspect of computer security. And security
              is the first listed value in rust's mission statement.
              
              Rust is not written as a pure expression based language. And as
              we all know very well from the experience with C and JS, any
              unexpected and weird looking code has the potential to hide great
              harm. Allowing programmers to stray too much from expected idioms
              is dangerous.
       
                keybored wrote 5 hours 29 min ago:
                We would need an example of puzzling code that can easily hide
                (security) bugs.
                
                The submission shows weird program snippets. I don’t think it
                shows weird snippets that can also easily hide bugs?
       
                steveklabnik wrote 7 hours 2 min ago:
                It is an aspect, but it Rust does not promise your core is
                secure.
                
                It’s not purely expression based but it is very close to it,
                there’s only    a few kinds of statements, the vast majority of
                things are expressions.
       
            PaulHoule wrote 7 hours 32 min ago:
            It is a critique of macros.  500 lines of Common Lisp replaces
            50,000 lines of C++ but those 500 lines make no sense at all the
            first time you see them.
       
        ramon156 wrote 9 hours 7 min ago:
        Note that for Rust devs these are also weird syntaxes. I feel like some
        people assume that an experienced dev can read these, but it takes a
        while to get what's going on.
       
          01HNNWZ0MV43FF wrote 4 hours 49 min ago:
          Yeah. I've been doing Rust for a few years and when I look at these I
          just see "Failed PR review, unreadable to humans"
       
          ChuckMcM wrote 8 hours 21 min ago:
          Kind of like obfuscated C code I suspect.
       
            dathinab wrote 6 hours 32 min ago:
            yes, but less risky (and less power full) because you often very
            fast can conclude that "whatever it does it's safe, sound and
            doesn't affect unrelated code"
       
              caim wrote 4 hours 38 min ago:
              And how would you conclude that "fast"?
              
              You can have UB in "safe rust". [1] You can even disable the Type
              check, trait check and borrow check in "safe rust"
              
              And all of this is unsound.
              
   URI        [1]: https://github.com/Speykious/cve-rs
   URI        [2]: https://users.rust-lang.org/t/i-finally-found-the-cheat-...
       
        behnamoh wrote 9 hours 9 min ago:
        Great, now this stuff gets fed into LLM trainings and we'll see them in
        the next-gen model outputs.
        
        Seriously though, I love "abusing" programming languages in unexpected
        ways. My favorite so far is: [1] . Reading this made me realize Elixir
        is literally macros all the way down, and it's a Lisp!
        
   URI  [1]: https://evuez.net/posts/cursed-elixir.html
       
        xyst wrote 9 hours 11 min ago:
        These “weird expressions” probably get used code golf.
       
          steveklabnik wrote 8 hours 54 min ago:
          Basically none of them are actually useful, or even do anything, it's
          mostly a parser stress test.
       
       
   DIR <- back to front page