Warum Rust nicht einfach erlaubt, String mit "hallo" zu vergleichen?
\n\nWenn Sie gerade erst anfangen, Rust zu lernen, und bereits auf die Notwendigkeit gestoßen sind, eine Variable vom Typ String mit einem String-Literal wie "hello" oder "world" abzugleichen, haben Sie sicherlich eine seltsame Compiler-Fehler gesehen. Sie schreiben so etwas wie:
let user_input = String::from("start");\nmatch user_input {\n "start" => println!("Los geht's!"),\n "stop" => println!("Anhalten!"),\n _ => println!("Unbekannter Befehl"),\n}\n\n\nUnd der Compiler gibt etwas aus wie: "expected struct `String`, found `&str`". Kommt Ihnen das bekannt vor?
\n\nAuf Stack Overflow hat die Frage "How to match a String against string literals?" Tausende von Aufrufen – das ist der wahre Schmerz von Anfängern. In diesem Artikel werden wir erklären, warum das passiert, und drei einfache Wege zeigen, das Problem zu lösen. Sie werden lernen, wie das Ownership- und Borrowing-System in Rust in der Praxis funktioniert, und werden keine Angst mehr vor Compiler-Fehlern haben.
\n\nDie Natur des Konflikts: String vs &str
\n\nUm das Problem zu verstehen, müssen wir uns die Grundlagen ins Gedächtnis rufen. In Rust gibt es zwei Haupttypen für die Arbeit mit Text:
\n\n- \n
String– dies ist ein besitzender String, der auf dem Heap gespeichert wird. Er kann verändert, vergrößert und verkleinert werden. Beispiel:let s = String::from("hello");\n &str– dies ist ein String-Slice. Es ist ein Verweis auf einen Teil eines Strings (oder auf ein String-Literal, das direkt in der Programm-Binärdatei gespeichert ist). Beispiel:"hello"ist genau&'static str. \n
Wenn Sie match user_input { "start" => ... } schreiben, versuchen Sie, einen String mit einem &str zu vergleichen. Der Compiler sagt: "Ich weiß nicht, wie ich sie direkt abgleichen soll, die Typen sind unterschiedlich!". Und das ist gut – Rust schützt Sie vor impliziten Konvertierungen, die zu Leistungseinbußen oder versteckten Fehlern führen könnten.
Was also tun? Es gibt drei einfache und elegante Lösungen.
\n\nLösung 1: Ausleihen mit as_str()
\n\nDer direkteste und verständlichste Weg ist, aus einem String einen String-Slice (&str) mit der Methode as_str() zu gewinnen. Diese Methode gibt einen Verweis auf den Inhalt des Strings zurück, ohne ihn zu kopieren. Das sieht so aus:
fn main() {\n let command = String::from("start");\n\n match command.as_str() {\n "start" => println!("Systemstart..."),\n "stop" => println!("Stopp..."),\n "pause" => println!("Pause..."),\n _ => println!("Unbekannter Befehl: {}", command),\n }\n}\n\n\nWarum funktioniert das? as_str() gibt ein &str zurück, das nun mit Literalen verglichen werden kann. Rust sieht, dass beide Operanden &str sind, und der Pattern-Matching funktioniert perfekt.
Vorteile: Einfachheit, Lesbarkeit, keine zusätzlichen Kosten (Ausleihen, nicht Kopieren).
\n\nNachteile: Man muss sich an den Methodenaufruf erinnern. Aber das ist eine Kleinigkeit.
\n\nLösung 2: Vergleich in einer if-Bedingung
\n\nWenn Sie nicht die volle Leistungsfähigkeit von match benötigen, können Sie einfache if-Bedingungen mit der Methode eq() oder dem Operator == verwenden. Rust kann String und &str über das Trait PartialEq vergleichen, das für dieses Typenpaar implementiert ist.
fn main() {\n let user_input = String::from("admin");\n\n if user_input == "admin" {\n println!("Zugriff gewährt!");\n } else if user_input == "guest" {\n println!("Gastzugang");\n } else {\n println!("Ungültiger Login");\n }\n}\n\n\nDas funktioniert, weil der Compiler beim Vergleich automatisch String in &str umwandelt. Aber in match gibt es diese Umwandlung nicht – dort wird eine exakte Übereinstimmung verlangt.