Warum lässt Rust Code mit dem falschen Rückgabetyp zu, jedoch nur mit einem abschließenden Semikolon?

8

Betrachten Sie den folgenden Rostcode:

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

Dies wird kompiliert (mit einer Warnung) und ausgeführt, obwohl der Rückgabetyp falsch ist. Es scheint, dass der Compiler mit dem Rückgabetyp ()in der letzten Zeile einverstanden ist, da er feststellt, dass dieser Code nicht erreichbar ist.

Wenn wir jedoch das letzte Semikolon entfernen:

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

Dann wird der Code nicht mehr kompiliert und gibt einen Tippfehler aus:

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)

Warum ist das? Ist der Rückgabetyp ()in diesen beiden Codeausschnitten nicht der gleiche ?


Hinweis: Ich möchte verstehen, warum sich der Rust-Compiler in diesen beiden Beispielen unterschiedlich verhält, dh wie der Rust-Compiler implementiert ist. Ich wollte keine philosophische Frage stellen, wie es sich aus Sicht des Sprachdesigns "verhalten" sollte (ich verstehe, dass eine solche Frage wahrscheinlich nicht zum Thema gehört).

6005
quelle
1
Der Rust-Compiler muss einen Typ für den Funktionskörper ableiten. Im ersten Fall gibt es keinen Rückgabeausdruck, und anscheinend leitet der Compiler !aufgrund der Endlosschleife, die Sinn macht , als Rückgabetyp. Im zweiten Fall gibt es einen Rückgabeausdruck, sodass der Typinferenzlöser diesen verwendet, um auf den Typ zu schließen, was ebenfalls sinnvoll ist. Ich denke nicht, dass dies in der Sprachreferenz angegeben ist, und ich denke auch nicht, dass es in irgendeiner Weise wichtig ist - lassen Sie einfach die nicht erreichbare Aussage weg und es wird Ihnen gut gehen.
Sven Marnach
@SvenMarnach Es ist immer noch eine Frage zur Sprache. Ich denke, das ist immer noch ein Thema.
Peter Hall
1
@SvenMarnach Ist das wirklich notwendig? Ich bin neu bei Rust und versuche zu verstehen, warum der Compiler das tut, was er tut. Ich bitte nicht um philosophische Antworten. Ich denke, Ihre Kommentare zeigen ein Missverständnis meiner Frage und tragen zur Wahrnehmung bei, dass SO giftig ist.
6005
@ 6005 Svens Kommentare haben nichts mit der "toxischen Kultur" zu tun, die SO vorgeworfen wurde (es sei denn, Sie glauben tatsächlich, dass er Sie aufgrund Ihres Geschlechts, Ihrer Sexualität, Ihrer Rasse usw. anders behandelt). Er hat zu einer (zivilisierten) Diskussion darüber beigetragen, ob Ihre Frage für SO geeignet ist oder nicht.
Peter Hall
3
Vielleicht war ich ein bisschen knapp, aber ich wollte auf keinen Fall beleidigen. Ich glaube nicht, dass irgendetwas mit Ihrer Frage nicht stimmt - ich habe es sogar genossen. :) Ich glaube einfach nicht, dass es eine "richtige" Antwort darauf gibt, da die Details der Typinferenz in Rust nicht angegeben sind. Wir können sehen, was Typinferenz in diesem Fall bewirkt , aber es gibt einfach keinen tieferen Grund dafür. Vielleicht könnte jemand mit intimen Kenntnissen der Compiler-Interna erklären, warum sich der Compiler so verhält, aber wir würden nicht viel über die Sprache lernen.
Sven Marnach

Antworten:

6

Der Rückgabetyp im ersten Codeblock ist tatsächlich !(nie genannt), da Sie eine Schleife haben, die niemals beendet wird (also gibt Rost eine Warnung aus, dass sie nicht erreichbar ist). Der vollständige Typ wäre:

fn f() -> !

Ich vermute, es !ist mehr wie der "untere" Typ in Rust als alles andere. Im zweiten Fall tritt Ihre Funktion wahrscheinlich in einem früheren Stadium während der Typprüfung aufgrund der Nichtübereinstimmung zwischen i32 und () auf, bevor der Compiler wie im ersten Beispiel zur Analyse der Nichterreichbarkeit gelangt.

Bearbeiten: Wie vorgeschlagen, ist hier der relevante Teil des Rostbuchs https://doc.rust-lang.org/book/ch19-04-advanced-types.html#the-never-type-that-never-returns

leshow
quelle
1
Es könnte hilfreich sein, auf den Abschnitt darüber im Rust-Buch zu verlinken . Im Einzelnen heißt es: „Der formale Weg , um dieses Verhalten zu beschreiben , ist , dass Ausdrücke vom Typ !kann in jeden anderen Typen umgewandelt werden.
Herohtar
Vielen Dank! Das ist interessant, ich werde darauf eingehen. Die Warnung hat jedoch nichts damit zu tun: Die Warnung ist nur eine "nicht erreichbare Aussage" (zeigt auf die letzte println!)
6005
Genau darauf habe ich angespielt. Die Anweisung ist nicht erreichbar und Ihr Rückgabetyp ist!
Leshow
Vielen Dank für Ihre Antwort, ich hoffe es ist richtig und ich akzeptiere keine falsche Erklärung :)
6005
Es ist richtig, lesen Sie den Link
Leshow
0

(Umwandlung von Svens erstem Kommentar in eine Antwort)

Der Rust-Compiler muss einen Typ für den Funktionskörper ableiten. Im ersten Fall gibt es keinen Rückgabeausdruck, und anscheinend schließt der Compiler daraus! als Rückgabetyp wegen der Endlosschleife, was Sinn macht. Im zweiten Fall gibt es einen Rückgabeausdruck, sodass der Typinferenzlöser diesen verwendet, um auf den Typ zu schließen, was ebenfalls sinnvoll ist.

Ich denke nicht, dass dies in der Sprachreferenz angegeben ist, und ich denke auch nicht, dass es in irgendeiner Weise wichtig ist - lassen Sie einfach die nicht erreichbare Aussage weg und es wird Ihnen gut gehen.

6005
quelle