6

I'm reading The Rust Programming Language book and I stumbled upon a simple expression:

let guess: u32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => continue,
};

How does match work with different kinds of expressions in its arms? E.g. the first arm would simply "return" num so that it's assigned to guess but in the second arm the expression is simply continue. How does match handle that and doesn't "assign" continue to guess but executes it? What happens with the whole assignment expression itself? Is it dropped from the call stack (if that's the correct term)?

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
stan0
  • 11,549
  • 6
  • 42
  • 59
  • 6
    I think you're missing context here; `continue` would be used in the context of a loop, in which case `guess` wouldn't hold a value and the loop would iterate to the next value - see: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=480e82fc7cd25b21144b95015478ed86 – Martin Gallagher Dec 29 '21 at 09:11
  • 1
    `continue` is not an expression (maybe that why it's kind of evil ? ^^) – Stargateur Dec 29 '21 at 09:17
  • Relevant: https://stackoverflow.com/questions/59013389/whats-the-difference-between-using-the-return-statement-and-omitting-the-semico – E_net4 Dec 29 '21 at 09:32
  • 6
    @Stargateur I think it's easier to think of `continue` as an expression of type `Never`, same as `return` and `break` – Ivan C Dec 29 '21 at 09:47
  • @MartinGallagher correct, it's used inside a loop but my confusion was what happens with `guess` and how the assignment is interrupted. – stan0 Dec 29 '21 at 09:52
  • @IvanC such evilness but from compiler view it must make thing more easy I guess. I still stand "avoid use such rudeness keyword" – Stargateur Dec 29 '21 at 10:25
  • See https://doc.rust-lang.org/rust-by-example/fn/diverging.html – Niklas Mohrin Dec 29 '21 at 10:29

1 Answers1

11

continue has a special type: it returns the never type, denoted !.

This type means "the code after that is unreachable". Since continue jumps to the next cycle of the loop, it'll never actually return any value (the same is true for return and break, and it's also the return type of panic!(), including all macros that panic: unreachable!(), todo!(), etc.).

The never type is special because it coerces (converts automatically) to any type (because if something cannot happen, we have no problems thinking about it as u32 or String or whatever - it will just not happen). This means it also unify with any other type, meaning the intersection of any type and ! is the other type.

match requires the expressions' type to unify (as does if). So your code returns the unification of ! and u32 == u32.

You can see that if you'll denote the type (requires nightly, since using the ! type not at return type position is experimental):

let num = match num {
    Ok(num) => {
        let num: i32 = num;
        num
    }
    Err(()) => {
        let never: ! = continue;
        never
    }
};

Playground.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • 1
    An infinite loop without `break`s also has type `!`, such as `loop {}`. I think this is the main reason the keyword `loop` was introduced, instead of using `while true` as others languages do. – rodrigo Dec 29 '21 at 13:13
  • 1
    @rodrigo historically `loop` wasn't an expression https://github.com/rust-lang/rfcs/blob/master/text/1624-loop-break-value.md. that said `loop` concept and the fact it's an expression now, is one of the best thing rust created. This made the `loop` go from "a nice way to express real infinite loop allowing semantically correct code" to "we create the best loop that ever existed in all previous programming language". – Stargateur Dec 29 '21 at 13:24
  • 1
    @GManNickG Yeah, I thought this was obvious. Edited. Thanks. – Chayim Friedman Dec 30 '21 at 07:02