Markdown fertig
This commit is contained in:
254
src/content/rust/02 - Higher-Lower-Spiel.mdx
Normal file
254
src/content/rust/02 - Higher-Lower-Spiel.mdx
Normal file
@ -0,0 +1,254 @@
|
||||
---
|
||||
title: Erstes Spiel
|
||||
published: 2022-10-18T17:56:26+02:00
|
||||
sorting: 2
|
||||
slug: higher-lower-game
|
||||
---
|
||||
|
||||
# Erstes Spiel
|
||||
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html)
|
||||
|
||||
## Projekt erstellen
|
||||
|
||||
Das Projekt wird wie in den vorherigen Einträgen beschrieben erstellt.
|
||||
|
||||
## Einen Input aufnehmen
|
||||
|
||||
```rust
|
||||
use std::io;
|
||||
|
||||
println!("Guess the number!");
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
||||
|
||||
println!("You guessed: {}", guess);
|
||||
```
|
||||
|
||||
Ausgabe:
|
||||
> Guess the number!
|
||||
> Please input your guess.
|
||||
> Number? 42
|
||||
> You guessed: 42
|
||||
|
||||
## Was haben wir gemacht?
|
||||
|
||||
* `use std::io;` bindet die Standard-IO Bibliothek ein
|
||||
* `let mut guess` legt eine Variable `guess` an
|
||||
- `mut` bedeutet, dass sie ``mutable'' also veränderbar ist
|
||||
* `String::new()` erstellt eine neue Instanz der `String`-Klasse
|
||||
* `io::stdin()` legt ein `Stdin`-Objekt an - ein Handler für die CLI-Eingabe
|
||||
- ohne die ``use'' Anweisung oben, müsste es `std::io::stdin()` sein
|
||||
* `.read_line(&mut guess)` ließt eine Zeile und speichert sie in guess
|
||||
- `&` erstellt dabei eine Referenz (wie in C)
|
||||
- Referenzen sind standardmäßig immutable - deshalb `&mut`
|
||||
- `read_line()` gibt ein `Result`-Objekt zurück, dieser kann `Ok` oder `Err` enthalten
|
||||
* `.expect("Fehlermeldung")` entpackt das `Result`-Objekt
|
||||
- Theoretisch ist das unnötig, sonst gibt es aber eine Warnung
|
||||
- Sollte ein `Err` im Result sein, wird durch `expect()` eine Exception auftreten
|
||||
* `println!("Eingabe: {}", guess)` ist ein formatiertes print
|
||||
|
||||
## Eine random Zahl erstellen
|
||||
|
||||
Für eine random Zahl brauchen wir die erste Dependency. +
|
||||
Also `Cargo.toml` bearbeiten:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
rand = "0.3.14"
|
||||
```
|
||||
|
||||
Dependencies findet man auch auf [crates.io](https://crates.io).
|
||||
Die crate `rand` kann jetzt im Code verwendet werden.
|
||||
|
||||
```rust
|
||||
extern crate rand;
|
||||
use rand::Rng;
|
||||
|
||||
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("{}", secret_number);
|
||||
```
|
||||
|
||||
Und schwubbs wird eine zufälle Zahl ausgegeben.
|
||||
|
||||
## Höher oder tiefer?
|
||||
|
||||
Vergleichen wir doch einfach mal…
|
||||
Aber was ist das? Ein Fehler??
|
||||
|
||||
<div class="notCompiling">
|
||||
```rust
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
```
|
||||
</div>
|
||||
|
||||
Der Compiler sagt uns dann Folgendes:
|
||||
|
||||
```rust
|
||||
match guess.cmp(&secret_number) {
|
||||
^^^^^^^^^^^^^^ expected struct `String`, found `u32`
|
||||
mismatched types
|
||||
```
|
||||
|
||||
Unser `guess` ist ja ein `String`! Den kann man nicht einfach mit einem
|
||||
`int` vergleichen (anscheinend).
|
||||
|
||||
Wir müssen unser guess also umwandeln:
|
||||
|
||||
```rust
|
||||
let guess: u32 = guess.trim().parse().expect("Please type a number!");
|
||||
```
|
||||
|
||||
`.strip()` entfernt Whitespace von beiden Seiten und `parse()` macht
|
||||
eine Zahl draus.
|
||||
|
||||
`guess` als Variable ist schon vorhanden? Kein Problem! Rust erlaubt
|
||||
``Shadowing'', damit man nicht mehrere Variablen unterschiedlicher
|
||||
Datentypen für den selben Wert anlegen muss.
|
||||
|
||||
Jetzt sollte das Vergleichen auch klappen!
|
||||
|
||||
```rust
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
```
|
||||
|
||||
> Too big!
|
||||
|
||||
Wuuh!
|
||||
|
||||
## Nicht nur ein Versuch
|
||||
|
||||
Damit wir mehrmals raten können, brauchen wir eine Schleife.
|
||||
|
||||
```rust
|
||||
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
||||
loop {
|
||||
let mut guess = String::new();
|
||||
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
||||
|
||||
let guess: u32 = guess.trim().parse().expect("Please type a number!");
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Number? 100
|
||||
> Too big!
|
||||
> Number? 50
|
||||
> Too big!
|
||||
> Number? 25
|
||||
> Too small!
|
||||
> Number? 30
|
||||
> Too small!
|
||||
> Number? 40
|
||||
> Too small!
|
||||
> Number? 42
|
||||
> Too small!
|
||||
> Number? 45
|
||||
> You win!
|
||||
> Number? 45
|
||||
> You win!
|
||||
> Number? 100
|
||||
> Too big!
|
||||
> Number? 45
|
||||
> You win!
|
||||
> Number?
|
||||
> ...
|
||||
|
||||
Funktioniert, aber selbst nach dem Erraten passiert nichts und wir
|
||||
sollen weiter raten. Offensichtlich müssen wir die Schleife noch abbrechen.
|
||||
|
||||
```rust
|
||||
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
||||
loop {
|
||||
let mut guess = String::new();
|
||||
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
||||
|
||||
let guess: u32 = guess.trim().parse().expect("Please type a number!");
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Number? 100
|
||||
> Too big!
|
||||
> Number? 50
|
||||
> Too big!
|
||||
> Number? 25
|
||||
> Too small!
|
||||
> Number? 42
|
||||
> Too big!
|
||||
> Number? 39
|
||||
> Too big!
|
||||
> Number? 37
|
||||
> Too big!
|
||||
> Number? 36
|
||||
> Too big!
|
||||
> Number? 33
|
||||
> Too big!
|
||||
> Number? 30
|
||||
> Too big!
|
||||
> Number? 29
|
||||
> You win!
|
||||
|
||||
## Error handling
|
||||
|
||||
Derzeit stirbt das Programm einfach mit einem Fehler, wenn man keine Zahl eingibt.
|
||||
Das können wir auch relativ einfach fixen:
|
||||
|
||||
```rust
|
||||
loop {
|
||||
let mut guess = String::new();
|
||||
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
||||
|
||||
let guess: u32 = match guess.trim().parse() {
|
||||
Ok(num) => num,
|
||||
Err(_) => continue,
|
||||
};
|
||||
// Wenn wir hier her kommen, haben wir eine gültige Zahl und beenden einfach.
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
> Number? a
|
||||
> Number? b
|
||||
> Number? 🦀
|
||||
> Number? 5
|
||||
|
||||
Statt einem `expect()` haben wir nun eine `match`-Expression. Die Syntax
|
||||
ist relativ einfach zu verstehen. Man kann auch mehrere `Ok(value)`
|
||||
nutzen, wobei dann das richtige aufgerufen wird. `Err(_)` nutzt den
|
||||
Unterstrich, um alle Fehler zu catchen, nicht nur einen speziellen.
|
||||
|
||||
Das `num` nach dem Pfeil ist ein implizites Return. Wenn eine Variable
|
||||
am Ende eines Blocks steht, wird sie zurückgegeben.
|
||||
|
||||
## Fertig
|
||||
|
||||
Wir haben nun alle Elemente für das "Higher-Lower-Game".
|
Reference in New Issue
Block a user