Hallo Bewohner! Mit dem offiziellen Rust Programming Language Guide können Sie schnellere und zuverlässigere Software erstellen. Hochrangige Ergonomie und niedrige Steuerelemente stehen häufig in Konflikt miteinander, aber Rust stellt diesen Konflikt in Frage.
Die Autoren des Buches sind Teil des Sprachentwicklungsteams. Dies bedeutet, dass Sie alle Informationen aus erster Hand erhalten - von der Installation der Sprache bis zur Erstellung zuverlässiger und skalierbarer Programme. Von der Erstellung von Funktionen über die Auswahl von Datentypen bis hin zur Bindung von Variablen gelangen Sie zu komplexeren Konzepten:
- Eigentum und Kreditaufnahme, Lebenszyklus und Typen.
- Garantierte Software-Sicherheit.
- Testen, Fehlerbehandlung und effektives Refactoring.
- Generika, intelligente Zeiger, Multithreading, Merkmalsobjekte und Zuordnungen.
- Arbeiten Sie mit dem integrierten Paketmanager Cargo zusammen, um Abhängigkeiten zu erstellen, zu testen, zu dokumentieren und zu verwalten.
- Erweiterte Tools für die Arbeit mit Unsafe Rust.
Sie finden viele Codebeispiele sowie drei Kapitel zum Erstellen vollständiger Projekte zur Festigung des Wissens: Ratespiele, Erstellen eines Befehlszeilentools und eines Multithread-Servers.
Für wen ist dieses Buch?
Wir gehen davon aus, dass Sie Ihren Code in einer anderen Programmiersprache geschrieben haben, machen jedoch keine Annahmen darüber, welche. Wir haben versucht, dieses Material für Personen mit einem breiten Spektrum an Programmierkenntnissen zugänglich zu machen. Wir werden keine Zeit damit verschwenden, darüber zu sprechen, was Programmierung ist. Wenn Sie ein absoluter Anfänger in der Programmierung sind, lesen Sie zuerst die Einführung in die Programmierung.
Wie man dieses Buch benutzt
-, , , . , ; .
: . . , , . 2, 12 20 , — .
1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .
5 , 6 , match if let. Rust .
7 (API). 8 , , , -. 9 .
10 , , , . 11 , Rust . 12 grep, . , .
13 — , . 14 Cargo . 15 , , , .
16 , Rust . 17 Rust - , , , .
18 , Rust. 19 , , Rust, , , .
20 , !
, . Rust, Rust, , , , Rust.
: -, ! - , , , . , , .
Rust — , : . , , . , , ! , , , , . , .
: . . , , . 2, 12 20 , — .
1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .
5 , 6 , match if let. Rust .
7 (API). 8 , , , -. 9 .
10 , , , . 11 , Rust . 12 grep, . , .
13 — , . 14 Cargo . 15 , , , .
16 , Rust . 17 Rust - , , , .
18 , Rust. 19 , , Rust, , , .
20 , !
, . Rust, Rust, , , , Rust.
: -, ! - , , , . , , .
Rust — , : . , , . , , ! , , , , . , .
Wo Muster verwendet werden können
In Rust erscheinen Muster an vielen Stellen, und Sie haben sie oft verwendet, ohne es zu merken! In diesem Abschnitt werden Situationen erläutert, in denen Muster gültig sind.
Ausdruckszweige abgleichen
Wie in Kapitel 6 erläutert, verwenden wir Muster in den Zweigen von Übereinstimmungsausdrücken. Formal werden Übereinstimmungsausdrücke als das Übereinstimmungsschlüsselwort, dann der zu vergleichende Wert und ein oder mehrere Zweige der Übereinstimmung definiert, die aus dem Muster und dem auszuführenden Ausdruck bestehen, wenn der Wert mit dem Muster dieses Zweigs übereinstimmt, zum Beispiel:
match {
=> ,
=> ,
=> ,
}
Eine der Anforderungen für Übereinstimmungsausdrücke ist, dass sie umfassend sein müssen, in dem Sinne, dass alle möglichen Werte in Übereinstimmung berücksichtigt werden müssen. Damit Sie alle möglichen Optionen berücksichtigen können, müssen Sie im letzten Zweig ein umfassendes Muster haben: Beispielsweise wird ein Variablenname, der einem beliebigen Wert entspricht, immer ausgelöst und deckt somit alle verbleibenden Fälle ab.
Das spezielle Muster _ passt zu allem, ist jedoch nicht an eine Variable gebunden und wird daher häufig in der letzten Hülle des Abgleichs verwendet. Das _-Muster ist beispielsweise nützlich, wenn Sie einen nicht angegebenen Wert ignorieren möchten. Wir werden das Muster im Abschnitt „Ignorieren von Werten in einem Muster“ genauer betrachten.
Wenn Bedingungen lassen
In Kapitel 6 haben wir if-Anweisungen hauptsächlich als kürzere Schreibweise für das Äquivalent eines Übereinstimmungsausdrucks erörtert, der nur einem Fall entspricht. Alternativ kann if let eine Übereinstimmung haben, die den auszuführenden Code enthält, wenn das Muster in if let nicht übereinstimmt.
Listing 18.1 zeigt, dass es auch möglich ist, if let-, else if- und else if-Anweisungen zu mischen und abzugleichen. Dies gibt uns mehr Flexibilität als die Verwendung eines Übereinstimmungsausdrucks, der nur einen Wert zum Vergleich mit Mustern ausdrücken kann. Darüber hinaus müssen Bedingungen in einer Reihe von if let-, else if- und else if-Anweisungen nicht aufeinander verweisen.
Der Code in Listing 18.1 zeigt eine Reihe von Tests für mehrere Bedingungen, die entscheiden, wie die Hintergrundfarbe aussehen soll. In diesem Beispiel haben wir Variablen mit fest codierten Werten erstellt, die ein reales Programm aus Benutzereingaben abrufen kann.
Listing 18.1. Mischen, wenn let, sonst if, else if let und else-Anweisungen
src/main.rs
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
(1) if let Some(color) = favorite_color {
(2) println!(" , {}, ", color);
(3) } else if is_tuesday {
(4) println!(" - !");
(5) } else if let Ok(age) = age {
(6) if age > 30 {
(7) println!(" ");
} else {
(8) println!(" ");
}
(9) } else {
(10) println!(" ");
}
}
Wenn der Benutzer eine Lieblingsfarbe (1) angibt, ist dies die Hintergrundfarbe (2). Wenn heute Dienstag (3) ist, ist die Hintergrundfarbe grün (4). Wenn der Benutzer sein Alter als Zeichenfolge angibt und wir es erfolgreich als Zahl (5) analysieren können, ist die Farbe je nach Wert der Zahl (6) entweder lila (7) oder orange (8). Wenn keine dieser Bedingungen zutrifft (9), ist die Hintergrundfarbe blau (10).
Diese bedingte Struktur ermöglicht komplexe Anforderungen. Mit den hier fest codierten Werten würde dieses Beispiel ausgegeben
.
Sie können sehen, dass ein if let-Ausdruck auf die gleiche Weise wie die Hülsen eines Übereinstimmungsausdrucks schattierte Variablen einführen kann: Die Codezeile if let Ok (age) = age (5) führt ein neues Alter für schattierte Variablen ein, das den Wert in der Ok-Variante enthält. Dies bedeutet, dass wir die Bedingung setzen müssen, wenn Alter> 30 in diesem Block (6): Wir können diese beiden Bedingungen nicht in der Anweisung kombinieren, wenn Ok (Alter) = Alter && Alter> 30 ist. Das schattierte variable Alter, das wir mit 30 vergleichen möchten , ist ungültig, bis der neue Bereich mit einer geschweiften Klammer beginnt.
Der Nachteil der Verwendung von if let-Anweisungen besteht darin, dass der Compiler nicht auf Vollständigkeit prüft, während dies bei Match-Anweisungen der Fall ist. Wenn wir den letzten else (9) -Block und damit die Behandlung einiger Fälle übersprungen hätten, hätte uns der Compiler nicht vor einem möglichen logischen Fehler gewarnt.
Während bedingte Schleifen lassen
Ähnlich wie bei der if let-Anweisung ermöglicht die while let-Bedingungsschleife, dass die while-Schleife ausgeführt wird, solange das Muster übereinstimmt. Das Beispiel in Listing 18.2 zeigt eine while-let-Schleife, die einen Vektor als Stapel verwendet und die Werte im Vektor in umgekehrter Reihenfolge der Reihenfolge ausgibt, in der sie hinzugefügt wurden.
Listing 18.2. Verwenden Sie eine while-let-Schleife, um Werte zu drucken, während stack.pop () Some zurückgibt
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
In diesem Beispiel werden 3, 2 und dann 1 gedruckt. Die Pop-Methode nimmt das letzte Element aus dem Vektor und gibt Some (Wert) zurück. Wenn der Vektor leer ist, gibt pop None zurück. Die while-Schleife führt den Code in ihrem Block weiter aus, bis pop Some zurückgibt. Wenn pop None zurückgibt, stoppt die Schleife. Wir können eine while-bedingte Schleife verwenden, um jedes Element vom Stapel zu entfernen.
Für Schleifen
In Kapitel 3 haben wir erwähnt, dass die for-Schleife das häufigste Schleifenkonstrukt im Rust-Code ist, aber wir haben das Muster für for noch nicht besprochen. In einer for-Schleife ist das Muster der Wert unmittelbar nach dem Schlüsselwort for. In x in y ist das Muster also x.
Listing 18.3 zeigt die Verwendung eines Musters in einer for-Schleife, um ein Tupel innerhalb einer for-Schleife zu zerstören oder zu zerlegen.
Listing 18.3. Verwenden eines Musters in einer for-Schleife, um ein Tupel zu zerstören
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} {}", value, index);
}
Der Code in Listing 18.3 zeigt Folgendes an:
0
b 1
2
Wir verwenden die Aufzählungsmethode, um den Iterator neu zu schreiben und den Wert und den Index dieses Werts im Iterator zu erzeugen, der in einem Tupel platziert ist. Der erste Aufruf der Enumerate-Methode erzeugt ein Tupel (0, 'a'). Wenn dieser Wert mit dem Muster (Index, Wert) kombiniert wird, der Index 0 ist und der Wert 'a' ist, wird die erste Datenzeile ausgegeben.
Lassen Sie Aussagen
Vor diesem Kapitel haben wir die Verwendung von Mustern nur mit Übereinstimmungs- und if-let-Anweisungen direkt erörtert. Tatsächlich haben wir jedoch Muster an anderer Stelle verwendet, einschließlich in let-Anweisungen. Stellen Sie sich eine einfache Möglichkeit vor, den Wert einer Variablen mit let zu übergeben:
let x = 5;
Wir haben diese Art von let-Anweisungen in diesem Buch hunderte Male verwendet, und obwohl Sie es vielleicht nicht bemerkt haben, haben Sie Muster verwendet! Formal gesehen sieht eine let-Anweisung folgendermaßen aus:
let = ;
In Anweisungen wie x = 5; mit einem Variablennamen im PATTERN-Slot ist der Variablenname nur eine einfache Form des Musters. Rust vergleicht den Ausdruck mit dem Muster und weist alle gefundenen Namen zu. Daher sei im Beispiel x = 5; Das Muster ist x, was bedeutet, "was hier mit der Variablen x übereinstimmt". Da der Name x das gesamte Muster darstellt, bedeutet dieses Muster effektiv "alles an die Variable x binden, unabhängig vom Wert".
Um die Zuordnung zum let-Anweisungsmuster klarer zu sehen, betrachten Sie Listing 18.4, in dem das let-Muster zum Destrukturieren eines Tupels verwendet wird.
Listing 18.4. Verwenden eines Musters, um ein Tupel zu zerstören und drei Variablen gleichzeitig zu erstellen
let (x, y, z) = (1, 2, 3);
Hier ordnen wir ein Tupel einem Muster zu. Rust vergleicht (1, 2, 3) mit (x, y, z) und stellt fest, dass dieser Wert mit dem Muster übereinstimmt, sodass Rust 1 mit x, 2 mit y und 3 mit z assoziiert. Sie können sich dieses Tupelmuster so vorstellen, dass drei separate variable Muster darin verschachtelt sind.
Wenn die Anzahl der Elemente im Muster nicht mit der Anzahl der Elemente im Tupel übereinstimmt, stimmt der Aggregattyp nicht überein und es wird ein Compilerfehler angezeigt. Listing 18.5 zeigt beispielsweise einen Versuch, ein Drei-Tupel in zwei Variablen zu zerlegen, was nicht funktioniert.
Listing 18.5. Falsche Konstruktion des Musters, dessen Variablen nicht mit der Anzahl der Elemente im Tupel übereinstimmen
let (x, y) = (1, 2, 3);
Der Versuch, diesen Code zu kompilieren, führt zu einem Fehler wie:
error[E0308]: mismatched types
--> src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ expected a tuple with 3 elements, found one with 2 elements
|
= note: expected type `({integer}, {integer}, {integer})`
found type `(_, _)`
Wenn wir einen oder mehrere Werte in einem Tupel ignorieren möchten, können wir _ oder .. verwenden, wie Sie im Abschnitt „Ignorieren von Werten in einem Muster“ sehen werden. Wenn das Problem darin besteht, dass das Muster zu viele Variablen enthält, müssen Sie die Typen übereinstimmen lassen, indem Sie die Variablen löschen, sodass die Anzahl der Variablen der Anzahl der Elemente im Tupel entspricht.
Funktionsparameter
Funktionsparameter können auch Muster sein. Der Code in Listing 18.6, der eine Funktion foo deklariert, die einen Parameter x vom Typ i32 verwendet, ist Ihnen jetzt bekannt.
Listing 18.6. Die Funktionssignatur verwendet Muster in Parametern
fn foo(x: i32) {
//
}
Teil x ist ein Muster! Wie bei let können wir das Tupel in den Funktionsargumenten dem Muster zuordnen. Listing 18.7 bricht die Werte im Tupel auf, sobald wir sie innerhalb der Funktion übergeben.
Listing 18.7. Funktion mit Parametern, die das Tupel zerstören
src/main.rs
fn print_coordinates(&(x, y): &(i32, i32)) {
println!(" : ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
Dieser Code wird ausgegeben
: (3, 5)
Die Werte & (3, 5) stimmen mit dem Muster & (x, y) überein, also ist x 3 und y ist 5.
Außerdem können wir Muster in Abschlussparameterlisten auf die gleiche Weise wie in Funktionsparameterlisten verwenden, da Verschlüsse ähneln Funktionen, wie in Kapitel 13 beschrieben.
Sie haben bereits verschiedene Möglichkeiten zur Verwendung von Mustern gesehen, aber sie funktionieren nicht überall gleich, wo Sie sie verwenden können. In einigen Situationen müssen diese Muster unwiderlegbar sein, in anderen können sie widerlegbar sein. Wir werden diese beiden Konzepte unten diskutieren.
Widerlegbarkeit: die Möglichkeit einer Musterfehlanpassung
Muster gibt es in zwei Varianten: widerlegbar und unwiderlegbar. Muster, die mit einem möglichen übergebenen Wert übereinstimmen, sind unwiderlegbar. Ein Beispiel ist das x in der Anweisung let x = 5;, weil x absolut mit allem übereinstimmt und daher nur übereinstimmen kann. Muster, die einigen der möglichen Bedeutungen nicht entsprechen, können widerlegt werden. Ein Beispiel ist Some (x) in der if if Some (x) = a_value-Anweisung. Wenn der Wert in a_value None und nicht Some ist, stimmen Some (x) nicht überein.
Funktionsparameter, let-Anweisungen und for-Schleifen können nur unwiderlegbare Muster akzeptieren, da das Programm nichts Sinnvolles tun kann, wenn die Werte nicht übereinstimmen. Die if let- und while let-Ausdrücke akzeptieren nur widerlegbare Muster, da sie per Definition so ausgelegt sind, dass sie einen möglichen Fehler behandeln: Die Funktionalität eines bedingten Ausdrucks ist seine Fähigkeit, je nach Erfolg oder Misserfolg unterschiedliche Aktionen auszuführen.
Im Allgemeinen sollten Sie sich keine Gedanken über die Unterscheidung zwischen widerlegbaren und unwiderlegbaren Mustern machen. Sie müssen sich jedoch des Konzepts der Widerlegbarkeit bewusst sein, um reagieren zu können, wenn Sie es in einer Fehlermeldung sehen. In diesen Fällen müssen Sie je nach dem beabsichtigten Verhalten des Codes entweder das Muster oder das Konstrukt ändern, mit dem Sie das Muster verwenden.
Schauen wir uns an, was passiert, wenn wir versuchen, ein unwiderlegbares Muster an einem Ort zu verwenden, an dem Rust ein unwiderlegbares Muster benötigt, und umgekehrt. Listing 18.8 zeigt eine let-Anweisung, aber für das von uns angegebene Muster Some (x) ein widerlegbares Muster. Wie zu erwarten, wird dieser Code nicht kompiliert.
Listing 18.8. Der Versuch, ein widerlegbares Muster mit let zu verwenden
let Some(x) = some_option_value;
Wenn some_option_value gleich None wäre, würde es nicht mit dem Some (x) -Muster übereinstimmen, dh das Muster ist widerlegbar. Eine let-Anweisung kann jedoch nur ein unwiderlegbares Muster akzeptieren, da der Code mit dem Wert None nichts Gültiges tun kann. Zum Zeitpunkt der Kompilierung wird sich Rust beschweren, dass wir versucht haben, ein widerlegbares Muster zu verwenden, wenn ein nicht widerlegbares Muster erforderlich ist:
error[E0005]: refutable pattern in local binding: `None` not covered
-->
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
Da wir nicht jeden gültigen Wert mit dem Some (x) -Muster abgedeckt haben (und auch nicht abdecken konnten!), Wirft Rust zu Recht einen Compilerfehler aus.
Um das Problem zu beheben, wenn wir ein widerlegbares Muster anstelle eines unwiderlegbaren Musters haben, können wir den Code ändern, der das Muster verwendet: Anstelle von let können wir if let verwenden. Wenn das Muster dann nicht übereinstimmt, wird der Code in geschweiften Klammern übersprungen und die Arbeit wird korrekt fortgesetzt. Listing 18.9 zeigt, wie Sie den Code in Listing 18.8 korrigieren.
Listing 18.9. Verwenden einer if let-Anweisung und eines widerlegbaren Musterblocks anstelle von let
if let Some(x) = some_option_value {
println!("{}", x);
}
Der Code ist fertig! Dies ist absolut korrekter Code, obwohl dies bedeutet, dass wir kein unwiderlegbares Muster ohne Fehler verwenden können. Wenn wir dem if let-Ausdruck ein Muster geben, das immer übereinstimmt, z. B. x, wie in Listing 18-10 gezeigt, wird er nicht kompiliert.
Listing 18.10. Versuch, ein unwiderlegbares Muster mit einer if let-Anweisung zu verwenden
if let x = 5 {
println!("{}", x);
};
Der Compiler beschwert sich, dass es keinen Sinn macht, einen if let-Ausdruck mit einem unwiderlegbaren Muster zu verwenden:
error[E0162]: irrefutable if-let pattern
--> <anon>:2:8
|
2 | if let x = 5 {
| ^ irrefutable pattern
Aus diesem Grund müssen die Hülsen eines Übereinstimmungsausdrucks widerlegbare Muster verwenden, mit Ausnahme der letzten Hülse, die alle verbleibenden Werte mit einem unwiderlegbaren Muster übereinstimmen muss. Mit Rust kann das unwiderlegbare Muster in einem Übereinstimmungsausdruck mit nur einer Hülse verwendet werden. Diese Syntax ist jedoch nicht besonders nützlich und kann durch eine einfachere let-Anweisung ersetzt werden.
Nachdem Sie nun wissen, wo Muster verwendet werden und wie sich widerlegbare und unwiderlegbare Muster unterscheiden, lernen Sie die Syntax kennen, mit der wir Muster erstellen können.
Über die Autoren
Steve Klabnik leitet das Rust-Dokumentationsteam und ist einer der wichtigsten Entwickler der Sprache. Er ist ein häufiger Dozent und schreibt viel Open Source Code. Zuvor arbeitete er an Projekten wie Ruby und Ruby on Rails.
Carol Nichols ist Mitglied des Rust Core-Entwicklungsteams und Mitbegründerin von Integer 32, LLC, dem weltweit ersten auf Rust ausgerichteten Beratungsunternehmen für Softwareentwicklung. Nichols ist der Organisator der Rust Belt-Konferenz über die Rust-Sprache.
»Weitere Details zum Buch finden Sie auf der Website des Herausgebers.
» Inhaltsverzeichnis
» Auszug
Für Habitaner 25% Rabatt auf den Gutschein - Rost
Nach Zahlung der Papierversion des Buches wird ein E-Book an die E-Mail gesendet.