Por que “break” não precisa de ponto e vírgula ao finalizar um “loop”?
Trecho do Capítulo 3.5 do Rust Book:
... usamos a
break
palavra-chave com o valorcounter * 2
. Após o loop, usamos um ponto-e-vírgula para encerrar a instrução que atribui o valor aresult
.
Além do snippet de código:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
Eu entendo como isso funciona e porque o resultado é 20, mas notei que se eu remover o ponto-e-vírgula na linha que contém a palavra-chave "break", o programa é equivalente.
Por que o ponto-e-vírgula é opcional neste caso?
Respostas
Um exemplo mais curto:
let _: i32 = loop {
if true {
break 3; // ()
}
};
Esse é apenas outro exemplo em que o ponto e vírgula não interfere com o resultado pretendido. Por um lado, a inserção de um ponto-e-vírgula introduz uma instrução de expressão, que avalia o tipo de unidade ()
. Como os loop
s e if
expressões continuam a admitir um bloco de código avaliando ao mesmo tipo ()
, em seguida, todos os tipos estão em conformidade.
let _: i32 = loop {
if true {
break 3 // !
}
};
Se o ponto-e-vírgula for removido, o break
será avaliado para o tipo nunca! , o que coage para qualquer outro tipo. Isso significa que ele atenderá a qualquer tipo esperado pelo escopo externo. Então está tudo bem da mesma forma, contanto que você não tente anexar nenhuma outra instrução antes do final do bloco if.
Ambos break
e return
avalie !
como, pois seus efeitos colaterais implicam que o programa não seguirá com o fluxo de trabalho natural.
Veja também:
- Por que as expressões de retorno usam ponto e vírgula quando são desnecessários?
- Qual é a diferença entre usar a instrução return e omitir o ponto-e-vírgula no Rust?
- Como declarar estaticamente o fim de uma função é inacessível
A referência da linguagem Rust nas declarações de expressão :
Uma instrução de expressão é aquela que avalia uma expressão e ignora seu resultado. Como regra, o objetivo de uma instrução de expressão é acionar os efeitos da avaliação de sua expressão.
Uma expressão que consiste em apenas uma expressão de bloco ou expressão de fluxo de controle, se usada em um contexto onde uma instrução é permitida, pode omitir o ponto-e-vírgula final .
Imagino que seja puramente por estética e ergonomia, já que quase tudo é uma expressão em Rust. Se o ponto-e-vírgula final fosse obrigatório após todas as expressões, teríamos que encerrar os blocos if-else (que também são expressões) com ponto-e-vírgula, o que parece péssimo:
if {
// do something
} else {
// do something else
}; // <- gross
Da mesma forma, podemos omitir o ponto-e-vírgula final em todas as expressões de fluxo de controle porque elas produzem fluxo de controle, então a função típica do ponto-e-vírgula, que é descartar o resultado da expressão e avaliar em ()
vez disso, se torna irrelevante.
fn five() -> i32 {
return 5 // to semicolon or not to semicolon? it doesn't matter
}
No exemplo acima, não faz diferença se terminarmos return 5
com um ponto-e-vírgula ou não, já que nada pode "capturar" o resultado dessa expressão, pois ela produz o fluxo de controle. O mesmo seria verdadeiro para outras expressões de fluxo de controle como break
e continue
.