First diary

This commit is contained in:
Daniel Kluge 2022-06-13 13:15:15 +02:00
parent 2fd290aa26
commit b002134c11
5 changed files with 454 additions and 0 deletions

3
diaries/rust.adoc Normal file
View File

@ -0,0 +1,3 @@
= Learning Rust
My progress documented. The diary is in German.

View File

@ -0,0 +1,47 @@
== Hello world
Hello world ist relativ einfach. `println!` ist ein Makro (eine
spezielle Art Funktion?), die einfach auf stdout printed.
~*In[2]:*~
[source, Rust]
----
println!("Hello world!");
----
~*Out[2]:*~
----
Hello world!
----
== Komplettes Programm
Rust hat ähnlich wie C eine `main`-Funktion, die zum Start ausgeführt
wird. +
Ein komplettes Programm zum Kompilieren hätte also den folgenden Inhalt:
~*In[3]:*~
[source, Rust]
----
fn main() {
println!("Hello world!");
}
----
Kompiliert und ausgeführt wird es dann über folgende Befehle:
[source,bash]
----
$ rustc main.rs
$ ./main
Hello world!
----
== Weitere Details
* `fn` -> Funktionsdeklaration
* 4 Leerzeichen zum Einrücken, kein Tab
* `;` am Ende der Zeile

View File

@ -0,0 +1,65 @@
== Cargo
Cargo ist Rusts package manager. +
Um ein neues Cargo-Projekt zu erstellen, braucht es das folgende
Command:
[source,bash]
----
$ cargo new projektname --bin
----
`--bin` sagt, dass wir ein neues Binary erstellen und keine
Bibliothek. +
Es wird auch gleich `main.rs`, ein `.git`-Ordner (inkl. `.gitignore`)
und `Cargo.toml` erstellt.
== Angelegte Datein
=== Cargo.toml
Unangetastet sieht die Datei so aus:
[source,toml]
----
[package]
name = "projektname"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
[dependencies]
----
Hier können also Meta-Infos wie Name und Dependencies gespeichert
werden.
=== main.rs
Die Main-Datei ist mit ``Hello World'' gefüllt.
== Commands
=== cargo build
[source,bash]
----
$ cargo build
$ ./target/debug/projektname
----
Standardmäßig wird ein Debug-Build erzeugt. `cargo build --release`
erzeugt einen Release-Build.
=== cargo run
Macht einen build und führt die Datei dann aus.
=== cargo check
Checkt alles einmal durch.
=== cargo update
Updatet alle Dependencies. Allerdings nur auf die letzte Subversion der
angegebenen Version. Will man eine neue Version, muss man das manuell
angeben.

View File

@ -0,0 +1,324 @@
== Erstes Spiel
=== Projekt erstellen
Das Projekt wird wie in Notebook 01 beschrieben erstellt.
== Einen Input aufnehmen
~*In[2]:*~
[source, Rust]
----
:dep evcxr_input
// Das ^ ist für Jupyter
// Das v würde man wirklich benutzen
// use std::io;
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = evcxr_input::get_string("Number? ");
// Das ^ ist für Jupyter
// Das v würde man wirklich benutzen
//let mut guess = String::new();
//io::stdin().read_line(&mut guess)
// .expect("Failed to read line");
println!("You guessed: {}", guess);
----
~*Out[2]:*~
----
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 imutable - 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:
[source,toml]
----
[dependencies]
rand = "0.3.14"
----
(In Jupyter müssen wir das anders lösen.)
Dependencies findet man auch auf https://crates.io[crates.io].
Die crate `rand` kann jetzt im Code verwendet werden.
~*In[3]:*~
[source, Rust]
----
:dep rand = "0.3.15"
// Das ^ ist von Jupyter
extern crate rand;
use rand::Rng;
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
println!("{}", secret_number);
----
~*Out[3]:*~
----
37
----
== Höher oder tiefer?
Vergleichen wir doch einfach mal… +
Ein Fehler?
~*In[4]:*~
[source, 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!"),
}
----
~*Out[4]:*~
----
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:
~*In[5]:*~
[source, 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!
~*In[6]:*~
[source, 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!"),
}
----
~*Out[6]:*~
----
Too big!
()----
== Nicht nur ein Versuch
Damit wir mehrmals raten können, brauchen wir eine Schleife.
~*In[7]:*~
[source, Rust]
----
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
loop {
let mut guess = evcxr_input::get_string("Number? ");
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!"),
}
}
----
~*Out[7]:*~
----
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?
thread '<unnamed>' panicked at 'Please type a number!: ParseIntError { kind: Empty }', src/lib.rs:133:43
stack backtrace:
0: rust_begin_unwind
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5
1: core::panicking::panic_fmt
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14
2: core::result::unwrap_failed
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/result.rs:1616:5
3: run_user_code_5
4: evcxr::runtime::Runtime::run_loop
5: evcxr::runtime::runtime_hook
6: evcxr_jupyter::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Segmentation fault.
Child process terminated with status: signal: 11 (core dumped)
----
Funktioniert, aber selbst nach dem Erraten passiert nichts und wir
sollen weiter raten. +
Offensichtlich müssen wir die Schleife dann abbrechen.
~*In[8]:*~
[source, Rust]
----
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
loop {
let mut guess = evcxr_input::get_string("Number? ");
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;
},
}
}
----
~*Out[8]:*~
----
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:
~*In[9]:*~
[source, Rust]
----
loop {
let mut guess = evcxr_input::get_string("Number? ");
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;
}
----
~*Out[9]:*~
----
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''.

View File

@ -14,5 +14,20 @@
"Have fun!"
],
"repo": "https://git.c0ntroller.de/c0ntroller/frontpage"
},
{
"type": "diary",
"name": "rust",
"short": "Me learning Rust (German).",
"desc": [
"I documented my progress to lern the Rust programming language.",
"The pages are exported from a #{jupyter notebook|https://jupyter.org/}, which I use to follow the Rust book.",
"Everything is in German as these are my own notes."
],
"entries": [
{ "title": "00 - Hello World", "filename": "00 - Hello World"},
{ "title": "01 - Cargo", "filename": "01 - Cargo"},
{ "title": "02 - Higher-Lower-Game", "filename": "02 - Higher-Lower-Spiel"}
]
}
]