Por que o Rust permite código com o tipo de retorno errado, mas apenas com um ponto e vírgula à direita?

8

Considere o seguinte código de ferrugem:

fn f() -> i32 {
    loop {
        println!("Infinite loop!");
    }
    println!("Unreachable");
}

Isso compila (com um aviso) e executa, apesar do tipo de retorno estar errado. Parece que o compilador está OK com o tipo de retorno da ()última linha, porque detecta que esse código está inacessível.

No entanto, se removermos o último ponto e vírgula:

fn f() -> i32 {
    loop {
        println!("Infinite loop!");
    }
    println!("Unreachable")
}

Em seguida, o código não é mais compilado, fornecendo um erro de tipo:

error[E0308]: mismatched types
  --> src/main.rs:14:5
   |
14 |     println!("Unreachable")
   |     ^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `()`
   |
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

Por que é isso? O tipo de retorno não é o mesmo ()em ambos os trechos de código?


Nota: Estou interessado em entender por que o compilador Rust se comporta de maneira diferente nesses dois exemplos, ou seja, como o compilador Rust é implementado. Eu não pretendia fazer uma pergunta filosófica sobre como "deveria" se comportar, da perspectiva do design da linguagem (eu entendo que essa pergunta provavelmente seria fora de tópico).

6005
fonte
1
O compilador Rust precisa inferir um tipo para o corpo da função. No primeiro caso, não há expressão de retorno e, aparentemente, o compilador deduz !o tipo de retorno por causa do loop infinito, o que faz sentido. No segundo caso, há uma expressão de retorno, então o solucionador de inferência de tipo usa isso para inferir o tipo, o que também faz sentido. Eu não acho que isso esteja especificado na referência de idioma, nem acho que isso importe de alguma forma - apenas omita a declaração inacessível e você ficará bem.
Sven Marnach
@SvenMarnach Ainda é uma pergunta sobre o idioma. Eu acho que ainda está no tópico.
Peter Hall
1
@SvenMarnach Isso é realmente necessário? Eu sou novo no Rust e estou tentando entender por que o compilador faz o que faz. Não estou pedindo respostas filosóficas. Acho que seus comentários mostram algum mal-entendido da minha pergunta e contribuem para a percepção de que o SO é tóxico.
6005 21/02
@ 6005 Os comentários de Sven não têm nenhuma relação com a "cultura tóxica" de que SO foi acusado (a menos que você realmente pense que ele está te tratando de maneira diferente por causa de seu sexo, sexualidade, raça etc.). Ele contribuiu para uma discussão (civilizada) sobre se sua pergunta é adequada ou não para SO.
Peter Hall
3
Talvez eu tenha sido um pouco concisa, mas certamente não quis ofender. Não acho que haja algo errado com sua pergunta - eu até gostei. :) Simplesmente não acho que exista uma resposta "certa", pois os detalhes da inferência de tipo no Rust não estão especificados. Podemos ver o que a inferência de tipo faz neste caso, mas simplesmente não existe uma razão mais profunda para isso. Talvez alguém com conhecimento íntimo dos componentes internos do compilador possa explicar por que o compilador se comporta dessa maneira, mas não aprenderíamos muito sobre o idioma.
Sven Marnach

Respostas:

6

O tipo de retorno no primeiro bloco de código é realmente !(chamado nunca) porque você tem um loop que nunca sai (portanto, o rust fornece um aviso dizendo que é inacessível). O tipo completo seria:

fn f() -> !

Eu suspeito que !é mais parecido com o tipo 'inferior' em Rust do que qualquer outra coisa. No segundo caso, sua função provavelmente falha em um estágio anterior durante a verificação de tipo devido à incompatibilidade entre i32 e () antes que o compilador chegue à análise de 'inacessibilidade', como ocorre no primeiro exemplo.

edit: como sugerido, aqui está a parte relevante do livro sobre ferrugem https://doc.rust-lang.org/book/ch19-04-advanced-types.html#the-never-type-that-never-returns

leshow
fonte
1
Pode ser útil vincular à seção sobre isso no livro Rust. Especificamente, ele afirma: "A maneira formal de descrever esse comportamento é que expressões do tipo !podem ser coagidas a qualquer outro tipo. "
Herohtar
Obrigado! Isso é interessante, vou ler sobre isso. No entanto, o aviso não está relacionado a isso: o aviso é apenas "declaração inacessível" (apontando para o último println!)
6005
Isso é exatamente o que eu estava fazendo alusão. A instrução está inacessível e seu tipo de retorno é!
leshow 21/02
Obrigado pela sua resposta, espero que esteja correto e não esteja aceitando uma explicação errada :)
6005 22/02
Está correto, leia o link
leshow 24/02
0

(Convertendo o primeiro comentário de Sven em uma resposta)

O compilador Rust precisa inferir um tipo para o corpo da função. No primeiro caso, não há expressão de retorno e, aparentemente, o compilador deduz! como o tipo de retorno por causa do loop infinito, o que faz sentido. No segundo caso, há uma expressão de retorno, então o solucionador de inferência de tipo usa isso para inferir o tipo, o que também faz sentido.

Eu não acho que isso esteja especificado na referência de idioma, nem acho que isso importe de alguma forma - apenas omita a declaração inacessível e você ficará bem.

6005
fonte