Einleitung
Rust ist eine leistungsstarke Systemprogrammiersprache, die aufgrund ihrer Sicherheit und Leistung an Popularität gewinnt. Allerdings verwirrt ihr System der Ownership und des Borrowings oft Anfänger. In diesem Artikel analysieren wir die Top-Fehler von Rust-Anfängern, damit Sie diese vermeiden und sich schneller mit der Sprache vertraut machen können. Wir betrachten typische Probleme mit Borrowing, Lifetimes, Strings und Speicherverwaltung.
1. Ignorieren der Borrow-Checker-Regeln
Der häufigste Fehler ist der Versuch, gleichzeitig eine veränderliche und eine unveränderliche Referenz auf dieselben Daten zu haben. Rust verbietet dies strikt zur Kompilierzeit.
Problem: Mehrfache Referenzen
fn main() { let mut data = vec![1, 2, 3]; let ref1 = &data; // unveränderliche Referenz let ref2 = &mut data; // veränderliche Referenz — FEHLER! println!("{} {}", ref1[0], ref2[0]);}Der Compiler gibt einen Fehler aus: cannot borrow `data` as mutable because it is also borrowed as immutable.
Lösung: Korrekter Gültigkeitsbereich
fn main() { let mut data = vec![1, 2, 3]; { let ref1 = &data; // unveränderliche Referenz println!("{}", ref1[0]); } // ref1 verlässt den Gültigkeitsbereich let ref2 = &mut data; // jetzt möglich ref2[0] = 10; println!("{}", ref2[0]);}Achten Sie stets auf die Lebensdauer von Referenzen. Wenn Sie sowohl mit einer veränderlichen als auch mit einer unveränderlichen Referenz arbeiten müssen, trennen Sie diese in verschiedene Blöcke.
2. Falscher Umgang mit Strings: String vs &str
Anfänger verwechseln oft String (besitzender Typ, veränderlich) und &str (Referenz auf einen String-Slice). Dies führt zu Fehlern bei der Übergabe von Strings an Funktionen.
Problem: Erwartung von Ownership statt Referenz
fn print_length(s: String) { println!("Länge: {}", s.len());}
fn main() { let name = "Alice".to_string(); print_length(name); println!("Name: {}", name); // FEHLER! name wurde verschoben}Lösung: Verwenden Sie &str für Parameter
fn print_length(s: &str) { println!("Länge: {}", s.len());}
fn main() { let name = "Alice".to_string(); print_length(&name); // Referenz übergeben println!("Name: {}", name); // funktioniert jetzt}Faustregel: Wenn eine Funktion keinen Besitz übernehmen soll, verwenden Sie &str. Dies macht den Code flexibel – Sie können sowohl &String als auch String-Literale "text" übergeben.
3. Fehler mit Lifetimes
Wenn eine Funktion eine Referenz zurückgibt, verlangt Rust die Angabe, wie lange diese Referenz leben wird. Anfänger vergessen oft die Lifetime-Annotationen.
Problem: Implizite Lifetimes
fn longest(x: &str, y: &str) -> &str { // FEHLER: missing lifetime specifier if x.len() > y.len() { x } else { y }}Lösung: Gemeinsame Lifetime hinzufügen
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y }}
fn main() { let s1 = String::from("kurz"); let s2 = "langer String"; let result = longest(&s1, &s2); println!("Längster: {}", result);}Die Annotation 'a teilt dem Compiler mit, dass die zurückgegebene Referenz nicht länger lebt als der kürzeste der Eingabeparameter. Dies garantiert Speichersicherheit.
4. Ignorieren von Option und Result
Rust hat kein null. Stattdessen werden Option<T> für mögliches Fehlen eines Wertes und Result<T, E> für die Fehlerbehandlung verwendet. Anfänger versuchen oft, Option ohne Prüfung zu dereferenzieren.
Problem: Panic bei unwrap
fn find_user(id: u32) -> Option<String> { if id == 1 { Some("Anna".to_string())