Por que “break” não precisa de ponto e vírgula ao finalizar um “loop”?

Nov 26 2020

Trecho do Capítulo 3.5 do Rust Book:

... usamos a breakpalavra-chave com o valor counter * 2. Após o loop, usamos um ponto-e-vírgula para encerrar a instrução que atribui o valor a result.

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

5 E_net4thecurator Nov 26 2020 at 22:30

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 loops e ifexpressõ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 breakserá 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 breake returnavalie !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
3 pretzelhammer Nov 26 2020 at 22:42

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 5com 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 breake continue.