Se eu tentar construir o seguinte código:
fn main () {
let my_val: u32 = 42;
match my_val % 2 {
0 => println!("We're even now"),
1 => println!("Well, that's odd"),
}
}
Terei a seguinte mensagem de erro:
error[E0004]: non-exhaustive patterns: `2_u32..=u32::MAX` not covered
--> src/main.rs:4:11
|
4 | match my_val % 2 {
| ^^^^^^^^^^ pattern `2_u32..=u32::MAX` not covered
|
= note: the matched value is of type `u32`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
6 ~ 1 => println!("Well, that's odd"),
7 ~ 2_u32..=u32::MAX => todo!(),
|
Eu realmente não entendi. Qual é o caso representado por 2_u32..=u32::MAX
?
Isso é realmente simples.
%
O operador é definido usandostd::ops::Rem
trait. Seu tipo associadoOutput
define qual é o resultado para essa operação. Na implementação, foru32
Output
éu32
. Então, da perspectiva do sistema de tipos,Rem::rem
pode retornar qualqueru32
valor. Portanto, para que match seja exaustivo, você deve corresponder a todos os valores.Você pode usar
unreachable
uma macro (que produzirá um pânico em tempo de execução se o caminho for alcançado) para indicar que esses caminhos são, bem, inacessíveis.Note que esse terceiro branch é muito fácil de otimizar, e que o compilador fará isso nas compilações de lançamento. Você pode examinar o ASM produzido usando goldbolt .
Se você se pergunta, por que eu tenho que fazer isso, ou por que Rust não pode simplesmente me deixar passar isso uma vez, note que este é um exemplo da filosofia geral do Rust de esperar explicitude. Mencionar que outras ramificações são inacessíveis resultará em nenhum ou insignificante custo de tempo de execução (e você sempre pode entrar em unsafe realm se realmente precisar), e pode capturar possíveis bugs futuros. Se por algum exemplo você começar a usar
mod 3
em vez demod 2
aritmética, ou passar este valor de alguma fonte externa e não verificada, então seu código ainda será compilado, mas este bug será capturado em tempo de execução.