¿Por qué Rust permite código con el tipo de retorno incorrecto, pero solo con un punto y coma final?

8

Considere el siguiente código Rust:

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

Esto compila (con una advertencia) y se ejecuta, a pesar de que el tipo de retorno es incorrecto. Parece que el compilador está bien con el tipo de retorno de ()en la última línea porque detecta que este código es inalcanzable.

Sin embargo, si eliminamos el último punto y coma:

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

Entonces el código ya no se compila, dando un error 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 qué es esto? ¿No es el mismo tipo de retorno (), en ambos fragmentos de código?


Nota: Estoy interesado en comprender por qué el compilador Rust se comporta de manera diferente en estos dos ejemplos, es decir, cómo se implementa el compilador Rust. No quise hacer una pregunta filosófica sobre cómo "debería" comportarse, desde la perspectiva del diseño del lenguaje (entiendo que tal pregunta probablemente estaría fuera de tema).

6005
fuente
1
El compilador Rust necesita inferir un tipo para el cuerpo de la función. En el primer caso, no hay expresión de retorno, y aparentemente el compilador infiere !como el tipo de retorno debido al bucle infinito, lo cual tiene sentido. En el segundo caso, hay una expresión de retorno, por lo que el solucionador de inferencia de tipos la usa para inferir el tipo, lo que también tiene sentido. No creo que esto esté especificado en la referencia del lenguaje, ni creo que importe de ninguna manera, solo omita la declaración inalcanzable y estará bien.
Sven Marnach
@SvenMarnach Sigue siendo una pregunta sobre el idioma. Creo que todavía está en el tema.
Peter Hall
1
@SvenMarnach ¿Es eso realmente necesario? Soy nuevo en Rust y estoy tratando de entender por qué el compilador hace lo que hace. No estoy pidiendo ninguna respuesta filosófica. Creo que sus comentarios muestran algunos malentendidos de mi pregunta y contribuyen a la percepción de que SO es tóxico.
6005
@ 6005 Los comentarios de Sven no tienen ninguna relación con la "cultura tóxica" de la que SO ha sido acusado (a menos que realmente piense que lo está tratando de manera diferente debido a su género, sexualidad, raza, etc.). Contribuyó a una discusión (civilizada) sobre si su pregunta es adecuada o no para SO.
Peter Hall
3
Tal vez era un poco conciso, pero ciertamente no tenía la intención de ofender. No creo que haya nada malo con tu pregunta, incluso la disfruté. :) Simplemente no creo que haya una respuesta "correcta", ya que no se especifican los detalles de la inferencia de tipos en Rust. Podemos ver qué tipo de inferencia hace en este caso, pero simplemente no hay ninguna razón más profunda para ello. Tal vez alguien con un conocimiento íntimo de los componentes internos del compilador podría explicar por qué el compilador se comporta de esta manera, pero no aprenderíamos mucho sobre el lenguaje.
Sven Marnach

Respuestas:

6

El tipo de retorno en el primer bloque de código es en realidad !(llamado nunca) porque tiene un bucle que nunca sale (por lo que el óxido le da una advertencia que dice que es inalcanzable). El tipo completo sería:

fn f() -> !

Sospecho que !se parece más al tipo 'inferior' en Rust que a cualquier otra cosa. En el segundo caso, es probable que su función se equivoque en una etapa anterior durante la verificación de tipo debido a la falta de coincidencia entre i32 y () antes de que el compilador llegue al análisis de 'inalcanzabilidad', como lo hace en el primer ejemplo.

editar: como se sugiere, aquí está la parte relevante del libro de óxido https://doc.rust-lang.org/book/ch19-04-advanced-types.html#the-never-type-that-never-returns

mostrar
fuente
1
Puede ser útil vincular a la sección sobre esto en el libro Rust. Específicamente, dice: "La forma formal de describir este comportamiento es que las expresiones de tipo !se pueden coaccionar a cualquier otro tipo " .
Herohtar
¡Gracias! Eso es interesante, leeré esto. Sin embargo, la advertencia no está relacionada con eso: la advertencia es solo una "declaración inalcanzable" (apuntando a la última println!)
6005
Eso es exactamente a lo que me refería. La declaración es inalcanzable, y su tipo de devolución es!
leshow
Gracias por su respuesta, espero que sea correcta y no estoy aceptando una explicación incorrecta :)
6005
Es correcto, lea el enlace
leshow
0

(Convertir el primer comentario de Sven en una respuesta)

El compilador Rust necesita inferir un tipo para el cuerpo de la función. En el primer caso, no hay expresión de retorno, ¡y aparentemente el compilador infiere! como el tipo de retorno debido al bucle infinito, lo que tiene sentido. En el segundo caso, hay una expresión de retorno, por lo que el solucionador de inferencia de tipos la usa para inferir el tipo, lo que también tiene sentido.

No creo que esto esté especificado en la referencia del lenguaje, ni creo que importe de ninguna manera, solo omita la declaración inalcanzable y estará bien.

6005
fuente