So meistern Sie das Rust-Vorstellungsgespräch: Top-Fragen für Fortgeschrittene
\n\nRust ist eine systemnahe Programmiersprache, die aufgrund ihrer Sicherheit, Leistung und des Fehlens eines Garbage Collectors an Popularität gewinnt. Wenn Sie die Grundlagen bereits beherrschen und sich auf ein Vorstellungsgespräch für eine Position als Senior- oder Middle-Rust-Entwickler vorbereiten, werden Sie auf Fragen stoßen, die über die einfache Syntax hinausgehen. In diesem Artikel analysieren wir die wichtigsten fortgeschrittenen Themen, die häufig in technischen Interviews vorkommen.
\n\nWir betrachten vier Blöcke: Ownership und Borrowing, Lifetimes, unsicherer Code (unsafe) und Makros sowie fortgeschrittenes Multithreading. Jeder Abschnitt enthält reale Fragen und Codebeispiele.
\n\n1. Ownership, Borrowing und Mutabilitätsregeln
\n\nFrage: Erklären Sie, wie der Ownership-Mechanismus in Rust funktioniert. Was ist der Unterschied zwischen &T, &mut T und Box<T>?
Antwort: Ownership ist die Grundlage der Speichersicherheit in Rust. Jeder Wert hat genau einen Besitzer. Wenn der Besitzer den Gültigkeitsbereich verlässt, wird der Wert zerstört. Borrowing (&T) erlaubt es, vorübergehend eine Referenz auf die Daten auszuleihen, ohne das Ownership zu übertragen. Mutables Borrowing (&mut T) gibt das Recht, die Daten zu ändern, jedoch nur, wenn keine anderen Referenzen vorhanden sind.
Box<T> ist ein intelligenter Zeiger, der Speicher auf dem Heap alloziert. Er überträgt das Ownership, was ihn von einfachen Referenzen unterscheidet.
fn main() {\n let mut s = String::from("Hallo");\n let r1 = &s; // immutable Referenz\n let r2 = &mut s; // Fehler! &mut und & können nicht gleichzeitig existieren\n // println!("{}", r1);\n}\n\n\nFrage: Was ist die „Regel der einen mutablen Referenz“?
\n\nAntwort: Zu jedem Zeitpunkt kann ein Wert entweder eine mutable Referenz oder eine beliebige Anzahl immutabler Referenzen haben, aber nicht beides gleichzeitig. Dies verhindert Datenrennen bereits zur Compile-Zeit.
\n\n2. Lifetimes – Wie der Compiler versteht, dass eine Referenz gültig ist
\n\nFrage: Wozu werden Lifetime-Annotationen benötigt? Geben Sie ein Beispiel, bei dem sie zwingend erforderlich sind.
\n\nAntwort: Lifetime-Annotationen ('a, 'b usw.) helfen dem Compiler zu überprüfen, dass alle Referenzen gültig sind. Sie sind zwingend erforderlich, wenn eine Funktion eine Referenz zurückgibt, die mit einem der Eingabeparameter verknüpft ist, und der Compiler die Lebensdauer nicht automatisch ableiten kann.
// Funktion gibt den längsten String zurück\nfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n if x.len() > y.len() { x } else { y }\n}\n\nfn main() {\n let s1 = String::from("lang");\n let result;\n {\n let s2 = String::from("kurz");\n result = longest(&s1, &s2); // ok\n }\n // println!("{}", result); // Fehler: s2 ist nicht mehr gültig\n}\n\n\nFrage: Was ist „Lifetime Elision“?
\n\nAntwort: Der Compiler fügt in einfachen Fällen automatisch Lifetime-Annotationen hinzu. Zum Beispiel versteht der Compiler bei der Methode fn first_word(s: &str) -> &str selbstständig, dass die Ausgabe-Lebensdauer der Eingabe-Lebensdauer entspricht. Die Elision funktioniert nach drei Regeln, die im Rust-Buch beschrieben sind.
3. Unsafe Rust und Makros
\n\nFrage: Wann ist die Verwendung von unsafe gerechtfertigt? Welche Operationen sind nur innerhalb von unsafe-Blöcken verfügbar?
Antwort: unsafe wird für Operationen verwendet, die der Compiler nicht überprüfen kann: Dereferenzieren von rohen Zeigern, Aufruf externer Funktionen (FFI), Zugriff auf mutable statische Variablen, Implementierung unsicherer Traits. Allerdings macht unsafe n