Just skimming through the Rust guide (guessing game), this code fragment doesn't seem right to me:
let num = match input_num {
Some(num) => num,
None => {
println!("Please input a number!");
continue;
}
};
How does type inference of num
work in this scenario? The first match case obviously return a number, whereas the second match case is just println
& continue
statement, which doesn't return anything(or return ()
). How does the compiler assume it's type safe?
Let's look at that piece of code more closely:
loop {
// ... some code omitted ...
let num = match input_num {
Some(num) => num,
None => {
println!("Please input a number!");
continue;
}
};
// ... some code omitted ...
}
The match statement is located inside a loop, and there are several constructs in the language which help control the looping process. break
exits from a loop early, while continue
skips the rest of the code in the loop and goes back to its beginning (restarts it). So this match above basically can be read basically as "Check the number, and if it is there, assign it to num
variable, otherwise output a message and restart from the beginning".
The behavior of "otherwise" branch is important: it ends with a control transfer operation, continue
in this case. The compiler sees continue
and knows that the loop is going to be restarted. Consequently, it does not really matter what value this branch yields, because it will never be used! It may as well never yield anything.
Such behavior often is modeled with so-called bottom type, which is a subtype of any type and which does not have values at all. Rust does not have subtyping (essentially), so such type is deeply magical. It is denoted as !
in type signatures:
fn always_panic() -> ! {
panic!("oops!")
}
This function always panics, which causes stack unwinding and eventual termination of the thread it was called in, so its return value, if there was one, will never be read or otherwise inspected, so it is absolutely safe not to return anything at all, even if it is used in expression context which expects some concrete type:
let value: int = always_panic();
Because always_panic()
has return type !
, the compiler knows that it is not going to return anything (in this case because always_panic()
starts stack unwinding), it is safe to allow it to be used in place of any type - after all, the value, even if it was there, is never going to be used.
continue
works exactly in the same way, but locally. None
branch "returns" type !
, but Some
branch returns value of some concrete numeric type, so the whole match statement is of this numeric type, because the compiler knows that None
branch will lead to control transfer, and its result, even if it had one, will never be used.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With