Einleitung: Warum Rust so streng ist und wie Code-Review hilft, nicht verrückt zu werden
\nSie haben Rust-Code geschrieben. Er wurde kompiliert – hurra! Aber das Problem: Der Senior-Entwickler seufzt, wenn er Ihren Pull Request öffnet, und beginnt, Kommentare zu schreiben. Kommt Ihnen das bekannt vor? Rust ist eine Sprache mit einer einzigartigen Philosophie: Sie vertraut Ihnen, überprüft aber alles. Allerdings sieht der Compiler nicht, was ein erfahrenes Auge sieht. Er wird nicht sagen: „Hey, das kann man eleganter machen“ oder „Ihr Code leckt – nicht Speicher, sondern Performance“.
\nIn diesem Artikel führen wir ein Rust-Code-Review durch und analysieren typische Anmerkungen von Seniors. Sie erfahren, wie Sie „einfach funktionierenden Code“ in idiomatischen und zuverlässigen Code verwandeln. Los geht's!
\n\n1. Ignorieren des Typsystems: „Ach, ich packe einfach alles in String“
\nDer häufigste Fehler von Anfängern ist die Verwendung von String oder generischen Typen, wo konkrete Typen benötigt werden. Ein Senior wird sagen: „Ihr Typ sollte die Geschäftslogik widerspiegeln, nicht einfach ein String sein“.
Problem: Verlust der Semantik
\nStellen Sie sich vor, Sie schreiben eine Funktion für die Arbeit mit E-Mail-Adressen:
\nfn send_email(to: String, subject: String, body: String) {\n // ...\n}\nWas ist hier falsch? Sie könnten to und subject verwechseln – der Compiler wird es nicht bemerken. Ein Senior würde das Newtype-Pattern vorschlagen:
struct Email(String);\nstruct Subject(String);\nstruct Body(String);\n\nfn send_email(to: Email, subject: Subject, body: Body) {\n // ...\n}\nScheint das überflüssig? Bei einem Rust-Code-Review ist dieser Ansatz der Goldstandard. Er macht den Code selbstdokumentierend und schließt eine ganze Klasse von Fehlern aus.
\n\n2. Übermäßiger Gebrauch von clone(): Kopieren bedeutet nicht Besitzen
\nJunior-Entwickler verwenden oft .clone() überall dort, wo der Compiler sich über Ausleihen beschwert. Ein Senior wird sagen: „Du gibst einfach vor dem Borrow Checker auf. Denk anders“.
Beispiel: Überflüssiges Clone
\nfn process(data: Vec<i32>) {\n let first = data[0].clone();\n let second = data[1].clone();\n // ...\n}\ni32 implementiert das Copy-Trait – clone() ist hier völlig überflüssig. Aber das Problem geht tiefer: Wenn Sie große Strukturen (z. B. Vec oder String) klonen, erhalten Sie einen Performance-Einbruch.
Der richtige Ansatz ist die Arbeit mit Referenzen:
\nfn process(data: &[i32]) {\n let first = &data[0];\n let second = &data[1];\n // ...\n}\nBei einem Rust-Code-Review müssen Sie erklären, warum Sie klonen. Wenn die Antwort „damit der Compiler nicht meckert“ lautet – bereiten Sie sich auf einen vernichtenden Kommentar vor.
\n\n3. Schlechte Fehlerbehandlung: .unwrap() ist keine endgültige Lösung
\nSeniors hassen unwrap() und expect() im Production-Code. Warum? Weil sie einen potenziell behebbaren Fehler in eine Panik verwandeln. Und eine Panik in Rust bedeutet einen Programmabsturz.
Was ist falsch an diesem Code?
\nlet file_content = std::fs::read_to_string("config.txt").unwrap();\nWenn die Datei nicht existiert – stürzt das Programm ab. Ein Senior würde vorschlagen:
\nlet file_content = std::fs::read_to_string("config.txt")\n .map_err(|e| format!("Failed to read config: {}", e))?;\nVerwenden Sie den ?-Operator und eigene Fehlertypen. Das ist die Grundlage von idiomatischem Rust. Bei einem Rust-Code-Review ist dies eine der ersten Anmerkungen.
4. Vergessene Performance: Allokationen und Iteratoren
\nRust ist eine Systemsprache. Ein Senior wird nicht nur auf Sicherheit, sondern auch auf Geschwindigkeit achten. Ein typisches Problem ist die Erstellung überflüssiger Collections.
\nBeispiel: Unnötiges Sammeln
\nlet numbers: Vec<i32> = (0..100).collect();\nlet doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();