First diary
This commit is contained in:
parent
2fd290aa26
commit
b002134c11
3
diaries/rust.adoc
Normal file
3
diaries/rust.adoc
Normal file
@ -0,0 +1,3 @@
|
||||
= Learning Rust
|
||||
|
||||
My progress documented. The diary is in German.
|
47
diaries/rust/00 - Hello World.adoc
Normal file
47
diaries/rust/00 - Hello World.adoc
Normal 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
|
65
diaries/rust/01 - Cargo.adoc
Normal file
65
diaries/rust/01 - Cargo.adoc
Normal 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.
|
324
diaries/rust/02 - Higher-Lower-Spiel.adoc
Normal file
324
diaries/rust/02 - Higher-Lower-Spiel.adoc
Normal 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''.
|
15
list.json
15
list.json
@ -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"}
|
||||
]
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue
Block a user