Ownership und Referenzen
Referenz auf lokale Variable
Abschnitt betitelt „Referenz auf lokale Variable“Programm 1:
/// Erstellt einen String zur Trennung von Textzeilen,/// gibt einen Standardwert zurück, wenn der bereitgestellte String leer istfn make_separator(user_str: &str) -> &str { if user_str == "" { let default = "=".repeat(10); &default } else { user_str }}Wenn Sie versuchen würden, diese Funktion zu kompilieren, welche der folgenden Beschreibungen trifft am besten auf den Compilerfehler zu, den Sie erhalten würden?
Da default auf dem Stack innerhalb von make_separator lebt, wird es freigegeben, sobald ein Aufruf von make_separator endet.
Dies führt dazu, dass &default auf freigegebenen Speicher zeigt. Rust bemängelt daher, dass Sie keine Referenz auf eine lokale Variable zurückgeben können.
Dangling Pointer
Abschnitt betitelt „Dangling Pointer“Programm 1:
/// Erstellt einen String zur Trennung von Textzeilen,/// gibt einen Standardwert zurück, wenn der bereitgestellte String leer istfn make_separator(user_str: &str) -> &str { if user_str == "" { let default = "=".repeat(10); &default } else { user_str }}Normalerweise gibt der Compiler beim Kompilieren dieser Funktion den folgenden Fehler zurück:
error[E0515]: cannot return reference to local variable `default` --> test.rs:6:9 |6 | &default | ^^^^^^^^ returns a reference to data owned by the current functionAngenommen, der Compiler hätte diese Funktion NICHT abgelehnt. Welches (wenn überhaupt) der folgenden Programme würde (1) den Compiler passieren und (2) möglicherweise undefiniertes Verhalten verursachen, wenn es ausgeführt wird? Markieren Sie jedes Programm, das beide Kriterien erfüllt, ODER markieren Sie “Keines dieser Programme”, wenn keines davon zutrifft.
Der Aufrufer muss einen leeren String übergeben, um die problematische if-Bedingung auszulösen. Dies gibt einen Dangling Pointer zurück.
Behebung von Lebenszeitproblemen
Abschnitt betitelt „Behebung von Lebenszeitproblemen“Programm 1:
/// Erstellt einen String zur Trennung von Textzeilen,/// gibt einen Standardwert zurück, wenn der bereitgestellte String leer istfn make_separator(user_str: &str) -> &str { if user_str == "" { let default = "=".repeat(10); &default } else { user_str }}Welche der folgenden Korrekturen (hervorgehoben) erfüllt diese drei Kriterien am besten:
- Die korrigierte Funktion besteht den Rust-Compiler,
- Die korrigierte Funktion bewahrt die Absicht des ursprünglichen Codes, und
- Die korrigierte Funktion führt keine unnötigen Ineffizienzen ein
Es gibt keine gültige Möglichkeit, einen Zeiger auf eine stack-allozierte Variable zurückzugeben. Die einfache Lösung besteht daher darin, den Rückgabetyp in String zu ändern
und den Eingabestring user_str in einen eigenen String zu kopieren. Die Anforderung, dass user_str ein String sein muss, würde jedoch die Flexibilität der API verringern, z.B.
könnte ein Aufrufer make_separator nicht für einen Substring eines größeren Strings aufrufen. Es würde auch erfordern, dass Aufrufer Strings auf dem Heap allozieren, z.B.
könnten sie kein String-Literal wie make_separator("Rust") verwenden.
Die idiomatischste Lösung für dieses Problem verwendet ein Konstrukt, das Sie noch nicht gesehen haben: Cow.
Der Clone-on-Write Smart Pointer würde es dieser Funktion ermöglichen, entweder einen eigenen String oder eine String-Referenz ohne Typfehler zurückzugeben.
Ownership und Option::unwrap
Abschnitt betitelt „Ownership und Option::unwrap“Programm 2:
/// Holt den String aus einem Option, falls vorhanden,/// andernfalls wird ein Standardwert zurückgegebenfn get_or_default(arg: &Option<String>) -> String { if arg.is_none() { return String::new(); } let s = arg.unwrap(); s.clone()}Wenn Sie versuchen würden, diese Funktion zu kompilieren, welche der folgenden Beschreibungen trifft am besten auf den Compilerfehler zu, den Sie erhalten würden?
Die Funktion Option::unwrap erwartet self, was bedeutet, dass sie Ownership von arg erwartet. arg ist jedoch eine unveränderliche
Referenz auf ein Option, sodass es die Ownership des Option nicht bereitstellen kann. Daher bemängelt der Compiler, dass wir
arg nicht über unwrap verschieben können.
Doppelte Freigabe
Abschnitt betitelt „Doppelte Freigabe“Programm 2:
/// Holt den String aus einem Option, falls vorhanden,/// andernfalls wird ein Standardwert zurückgegebenfn get_or_default(arg: &Option<String>) -> String { if arg.is_none() { return String::new(); } let s = arg.unwrap(); s.clone()}Normalerweise gibt der Compiler beim Kompilieren dieser Funktion den folgenden Fehler zurück:
error[E0507]: cannot move out of `*arg` which is behind a shared reference --> test.rs:7:13 |7 | let s = arg.unwrap(); | ^^^^-------- | | | | | `*arg` moved due to this method call | help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents | move occurs because `*arg` has type `Option<String>`, which does not implement the `Copy` traitAngenommen, der Compiler hätte diese Funktion NICHT abgelehnt. Welches (wenn überhaupt) der folgenden Programme würde (1) den Compiler passieren und (2) möglicherweise undefiniertes Verhalten verursachen, wenn es ausgeführt wird? Markieren Sie jedes Programm, das beide Kriterien erfüllt, ODER markieren Sie “Keines dieser Programme”, wenn keines davon zutrifft.
Alle diese Programme verletzen die Speichersicherheit aufgrund einer doppelten Freigabe (double-free). Wenn arg.unwrap() erlaubt wäre, dann
würde die Ownership des Strings String::from("Rust") von s übernommen. Nach der Rückgabe von get_or_default
würde der String freigegeben. opt glaubt jedoch auch, den String zu besitzen, sodass
der String ein zweites Mal im Namen von opt freigegeben würde.
Idiomatisches Option Handling
Abschnitt betitelt „Idiomatisches Option Handling“Programm 2:
/// Holt den String aus einem Option, falls vorhanden,/// andernfalls wird ein Standardwert zurückgegebenfn get_or_default(arg: &Option<String>) -> String { if arg.is_none() { return String::new(); } let s = arg.unwrap(); s.clone()}Welche der folgenden Korrekturen (hervorgehoben) erfüllt diese drei Kriterien am besten:
- Die korrigierte Funktion besteht den Rust-Compiler,
- Die korrigierte Funktion bewahrt die Absicht des ursprünglichen Codes, und
- Die korrigierte Funktion führt keine unnötigen Ineffizienzen ein
Die Kombination von is_none und unwrap ist hier ein Rust-Anti-Pattern, da ein match die beiden Funktionalitäten kombiniert und
automatisch das Weiterreichen der Referenz &Option in das Innere handhabt, um &String zu erzeugen. Daher ist die match-Lösung
die idiomatischste und besteht den Compiler, ohne die beabsichtigte Typensignatur der Funktion zu ändern.
Die Lösung, &Option in Option zu ändern, ist nicht wünschenswert, da sie vom Aufrufer verlangt, die Ownership seines Option bereitzustellen,
was eine wesentlich restriktivere API darstellt.