Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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?






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<T, !> without checking for errors, and the optimizer can remove the error-checking branches from generic code: https://doc.rust-lang.org/std/primitive.never.html

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.


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.

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.



Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: