Praxisteil & Übungen: Einrichtung & Erstes Programm
In diesem Praxisteil vertiefen wir die in Kapitel 2 gelernten Konzepte rund um die Projektverwaltung mit Cargo, das Einbinden externer Bibliotheken (Crates) und den syntaktischen Unterschied zwischen gewöhnlichen Funktionen und Makros.
1. Praxis-Szenario: Der Zufalls-Token-Generator für ein E-Commerce-Startup
Stellen Sie sich vor, Sie arbeiten als Softwareentwickler in einem jungen E-Commerce-Startup. Ihre erste Aufgabe besteht darin, einen Prototyp für einen Zufalls-Token-Generator zu erstellen. Dieser Generator soll später eindeutige, zufällige Sitzungs-IDs oder Gutscheincodes für Kunden erzeugen.
Um dieses Szenario umzusetzen, greifen wir auf das bewährte Community-Paket rand zurück und geben eine Begrüßungsnachricht sowie eine zufällige Zahl auf dem Terminal aus.
Die Übungsaufgabe befindet sich im Verzeichnis:
Die dazugehörige Konfigurationsdatei der Übung finden Sie hier:
2. Genaue Code-Erklärung der Übungsaufgabe
Lassen Sie uns die fehlerhafte Ausgangsdatei exercises/00_setup/src/main.rs von Anfang bis Ende Zeile für Zeile genau betrachten:
1: // Übung 0: Setup, Cargo und Makros
2: // Beheben Sie die Compilerfehler in dieser Datei, damit das Programm läuft!
3:
4: fn main() {
5: // 1. FEHLER: Das println-Makro wird ohne Ausrufezeichen aufgerufen.
6: // Fügen Sie das Ausrufezeichen hinzu!
7: println("Willkommen beim ersten Cargo-Projekt!");
8:
9: // 2. FEHLER: Dieses Codestück verwendet das `rand` Crate, welches jedoch
10: // nicht in der Cargo.toml deklariert ist. Fügen Sie `rand = "0.8.5"`
11: // in der Cargo.toml unter [dependencies] hinzu!
12: let zufallszahl = rand::random::<u8>();
13: println!("Ihre zufällige Zahl: {}", zufallszahl);
14: }
15:
16: #[cfg(test)]
17: mod tests {
18: #[test]
19: fn test_setup_success() {
20: assert_eq!(1 + 1, 2);
21: }
22: }
Zeilen-Analyse der Übung:
- Zeile 1–2: Reine Kommentare zur Einleitung und Arbeitsanweisung.
- Zeile 4: Die Deklaration der Hauptfunktion
fn main(), welche den Einstiegspunkt jeder ausführbaren Rust-Anwendung darstellt. - Zeile 7:
println("...");– Hier liegt der erste Compiler-Fehler. Der Entwickler möchte Text ausgeben, ruftprintlnjedoch wie eine normale Funktion auf. - Zeile 12:
let zufallszahl = rand::random::<u8>();– Hier deklarieren wir eine unveränderliche Variablezufallszahl. Wir rufen die Funktionrandomaus dem Namensraumrandauf und spezifizieren über die “Turbofisch-Syntax”::<u8>, dass wir eine 8-Bit-Zufallszahl ohne Vorzeichen (Wertebereich 0 bis 255) erzeugen wollen. Da das Craterandin derCargo.tomlfehlt, kann der Compiler dieses Modul nicht finden. - Zeile 13:
println!("Ihre zufällige Zahl: {}", zufallszahl);– Eine korrekte Makro-Ausgabe, die den Inhalt vonzufallszahlan die Stelle des Platzhalters{}formatiert. - Zeile 16–22: Ein internes Testmodul
tests, das nur im Testmodus (cargo test) kompiliert wird. Es enthält einen einfachen, immer erfolgreichen Zusicherungstest (assert_eq!(1 + 1, 2)).
3. Schritt-für-Schritt-Anleitung zur Fehlerbehebung
Wenn Sie das Projekt im Ausgangszustand kompilieren (cargo check), bricht der Compiler mit Fehlermeldungen ab. Wir beheben diese in zwei exakten Schritten:
Schritt 1: Das Makro-Ausrufezeichen hinzufügen
Fehlermeldung des Compilers:
error[E0423]: expected function, found macro `println`
--> src/main.rs:7:5
|
7 | println("Willkommen beim ersten Cargo-Projekt!");
| ^^^^^^^ not a function
|
help: use `!` to invoke the macro
|
7 | println!("Willkommen beim ersten Cargo-Projekt!");
| +
Ursachen-Erklärung:
In Rust sind Makros syntaktisch streng von Funktionen getrennt. Normale Funktionen haben eine feste Anzahl von Parametern mit fest definierten Typen. Makros (gekennzeichnet durch das !) hingegen verarbeiten Syntaxstrukturen zur Kompilierzeit und können eine variable Anzahl von Argumenten mit unterschiedlichen Typen entgegennehmen (wie println!). Der Compiler erkennt println ohne ! als Funktionsaufruf, findet jedoch keine Funktion dieses Namens.
Behebung: Fügen Sie in Zeile 7 ein Ausrufezeichen an das Wort println an, sodass es zu println! wird.
Schritt 2: Die externe Abhängigkeit in Cargo.toml deklarieren
Fehlermeldung des Compilers:
error[E0433]: failed to resolve: use of undeclared crate or module `rand`
--> src/main.rs:12:23
|
12 | let zufallszahl = rand::random::<u8>();
| ^^^^ use of undeclared crate or module `rand`
Ursachen-Erklärung:
Der Compiler liest den Code und stößt in Zeile 12 auf das Modul rand. Da Rust-Projekte standardmäßig nur Zugriff auf die Standardbibliothek (std) haben, müssen alle externen Pakete explizit im Paketmanager deklariert werden. Da unter [dependencies] in der Cargo.toml kein Eintrag für rand existiert, weiß der Compiler nicht, woher er die Funktionsdefinitionen nehmen soll.
Behebung: Öffnen Sie die Datei exercises/00_setup/Cargo.toml und fügen Sie unter der Sektion [dependencies] die Zeile rand = "0.8.5" hinzu.
4. Genaue Code-Erklärung der Musterlösung
Nach Durchführung der Modifikationen sieht die korrekte Musterlösung unter solutions/00_setup/src/main.rs wie folgt aus:
1: // Musterlösung zu Übung 0: Setup, Cargo und Makros
2: // Alle Compilerfehler wurden erfolgreich behoben.
3:
4: fn main() {
5: // 1. LÖSUNG: Das Ausrufezeichen '!' wurde hinzugefügt, um das Makro println! korrekt aufzurufen.
6: println!("Willkommen beim ersten Cargo-Projekt!");
7:
8: // 2. LÖSUNG: Die Abhängigkeit 'rand = "0.8.5"' wurde in der Cargo.toml hinzugefügt.
9: // Dadurch wird das Crate beim Build-Vorgang heruntergeladen und steht uns hier zur Verfügung.
10: let zufallszahl = rand::random::<u8>();
11: println!("Ihre zufällige Zahl: {}", zufallszahl);
12: }
13:
14: #[cfg(test)]
15: mod tests {
16: #[test]
17: fn test_setup_success() {
18: assert_eq!(1 + 1, 2);
19: }
20: }
Funktionsweise der Lösung:
- Zeile 6: Das Makro
println!gibt nun fehlerfrei den Begrüßungstext im Terminal aus. - Zeile 10: Durch die Deklaration in der
Cargo.tomllädt Cargo beim Kompilieren das Paketrandund seine Transitiv-Abhängigkeiten aus der Paketregistrierung herunter. Der Aufrufrand::random::<u8>()funktioniert nun fehlerfrei und liefert zur Laufzeit eine zufällige Zahl vom Typu8(Bereich0bis255), welche inzufallszahlgebunden wird. - Zeile 11: Die Zufallszahl wird erfolgreich über den Formatierungsplatzhalter
{}auf dem Terminal ausgegeben.
Sie können die Korrektheit der Lösung überprüfen, indem Sie im Verzeichnis der Lösung cargo run oder cargo test ausführen.
Die vollständige Musterlösung finden Sie hier: