Markdown fertig
This commit is contained in:
parent
592c83cfe2
commit
e8dba73ea9
@ -30,7 +30,7 @@ My NAS is already connected to the Shield so...
|
||||
|
||||
## The Solution
|
||||
|
||||
I already had some experience with the Google API from projects like the [Infoscreen](/blog/projects/infoscreen) and the [Simple Callback Server](/blog/projects/simple-cb).
|
||||
I already had some experience with the Google API from projects like the [Infoscreen](/blog/infoscreen) and the [Simple Callback Server](/blog/simple-cb).
|
||||
|
||||
I decided to make it easier for users who are not familiar with the API, so I created a CLI to set everything up.
|
||||
|
||||
|
@ -20,9 +20,7 @@ println!("Hello world!");
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Hello world!
|
||||
```
|
||||
> Hello world!
|
||||
|
||||
## Komplettes Programm
|
||||
|
||||
|
@ -1,116 +1,86 @@
|
||||
:experimental:
|
||||
:docdatetime: 2022-10-18T17:56:26+02:00
|
||||
---
|
||||
title: Erstes Spiel
|
||||
published: 2022-10-18T17:56:26+02:00
|
||||
sorting: 2
|
||||
slug: higher-lower-game
|
||||
---
|
||||
|
||||
= Erstes Spiel
|
||||
# Erstes Spiel
|
||||
|
||||
_https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html[Link zum Buch]_ | _Diese Seite ist aus einem https://jupyter.org/[Jupyter Notebook] exportiert_.
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html)
|
||||
|
||||
== Projekt erstellen
|
||||
## Projekt erstellen
|
||||
|
||||
Das Projekt wird wie in Notebook 01 beschrieben erstellt.
|
||||
Das Projekt wird wie in den vorherigen Einträgen beschrieben erstellt.
|
||||
|
||||
== Einen Input aufnehmen
|
||||
## 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;
|
||||
```rust
|
||||
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");
|
||||
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
|
||||
|
||||
~*Out[2]:*~
|
||||
----
|
||||
Guess the number!
|
||||
Please input your guess.
|
||||
Number? 42
|
||||
You guessed: 42
|
||||
----
|
||||
|
||||
== Was haben wir gemacht?
|
||||
## 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
|
||||
- `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
|
||||
* `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
|
||||
- `&` 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
|
||||
- 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
|
||||
## Eine random Zahl erstellen
|
||||
|
||||
Für eine random Zahl brauchen wir die erste Dependency. +
|
||||
Also `Cargo.toml` bearbeiten:
|
||||
|
||||
[source, toml]
|
||||
----
|
||||
```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].
|
||||
```
|
||||
|
||||
Dependencies findet man auch auf [crates.io](https://crates.io).
|
||||
Die crate `rand` kann jetzt im Code verwendet werden.
|
||||
|
||||
|
||||
~*In[3]:*~
|
||||
[source, rust]
|
||||
----
|
||||
:dep rand = "0.3.15"
|
||||
// Das ^ ist von Jupyter
|
||||
|
||||
```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.
|
||||
|
||||
~*Out[3]:*~
|
||||
----
|
||||
37
|
||||
----
|
||||
## Höher oder tiefer?
|
||||
|
||||
== Höher oder tiefer?
|
||||
Vergleichen wir doch einfach mal…
|
||||
Aber was ist das? Ein Fehler??
|
||||
|
||||
Vergleichen wir doch einfach mal… +
|
||||
Ein Fehler?
|
||||
|
||||
|
||||
~*In[4]:*~
|
||||
[source.notCompiling, rust]
|
||||
----
|
||||
<div class="notCompiling">
|
||||
```rust
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
@ -118,30 +88,25 @@ match guess.cmp(&secret_number) {
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
----
|
||||
```
|
||||
</div>
|
||||
|
||||
Der Compiler sagt uns dann Folgendes:
|
||||
|
||||
~*Out[4]:*~
|
||||
----
|
||||
|
||||
```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). +
|
||||
`int` vergleichen (anscheinend).
|
||||
|
||||
Wir müssen unser guess also umwandeln:
|
||||
|
||||
|
||||
~*In[5]:*~
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let guess: u32 = guess.trim().parse().expect("Please type a number!");
|
||||
----
|
||||
```
|
||||
|
||||
`.strip()` entfernt Whitespace von beiden Seiten und `parse()` macht
|
||||
eine Zahl draus.
|
||||
@ -152,10 +117,7 @@ Datentypen für den selben Wert anlegen muss.
|
||||
|
||||
Jetzt sollte das Vergleichen auch klappen!
|
||||
|
||||
|
||||
~*In[6]:*~
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
@ -163,26 +125,22 @@ match guess.cmp(&secret_number) {
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
> Too big!
|
||||
|
||||
~*Out[6]:*~
|
||||
----
|
||||
Too big!
|
||||
()
|
||||
----
|
||||
Wuuh!
|
||||
|
||||
== Nicht nur ein Versuch
|
||||
## Nicht nur ein Versuch
|
||||
|
||||
Damit wir mehrmals raten können, brauchen wir eine Schleife.
|
||||
|
||||
|
||||
~*In[7]:*~
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
||||
loop {
|
||||
let mut guess = evcxr_input::get_string("Number? ");
|
||||
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) {
|
||||
@ -191,46 +149,40 @@ loop {
|
||||
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?
|
||||
...
|
||||
----
|
||||
> 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 dann abbrechen.
|
||||
sollen weiter raten. Offensichtlich müssen wir die Schleife noch abbrechen.
|
||||
|
||||
|
||||
~*In[8]:*~
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
||||
loop {
|
||||
let mut guess = evcxr_input::get_string("Number? ");
|
||||
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) {
|
||||
@ -242,45 +194,39 @@ loop {
|
||||
},
|
||||
}
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
> 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!
|
||||
|
||||
~*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
|
||||
|
||||
== Error handling
|
||||
Derzeit stirbt das Programm einfach mit einem Fehler, wenn man keine Zahl eingibt.
|
||||
Das können wir auch relativ einfach fixen:
|
||||
|
||||
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]
|
||||
----
|
||||
```rust
|
||||
loop {
|
||||
let mut guess = evcxr_input::get_string("Number? ");
|
||||
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,
|
||||
@ -288,17 +234,12 @@ loop {
|
||||
// Wenn wir hier her kommen, haben wir eine gültige Zahl und beenden einfach.
|
||||
break;
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
|
||||
~*Out[9]:*~
|
||||
----
|
||||
Number? a
|
||||
Number? b
|
||||
Number? 🦀
|
||||
Number? 5
|
||||
()
|
||||
----
|
||||
> 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)`
|
||||
@ -308,6 +249,6 @@ 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
|
||||
## Fertig
|
||||
|
||||
Wir haben nun alle Elemente für das ``Higher-Lower-Game''.
|
||||
Wir haben nun alle Elemente für das "Higher-Lower-Game".
|
@ -1,40 +1,44 @@
|
||||
:experimental:
|
||||
:docdatetime: 2022-10-18T17:56:26+02:00
|
||||
---
|
||||
title: Konzepte
|
||||
published: 2022-10-18T17:56:26+02:00
|
||||
sorting: 3
|
||||
slug: konzepte
|
||||
---
|
||||
|
||||
= Konzepte
|
||||
# Konzepte
|
||||
|
||||
https://doc.rust-lang.org/book/ch03-00-common-programming-concepts.html[Link zum Buch]
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch03-00-common-programming-concepts.html)
|
||||
|
||||
|
||||
== Variablen
|
||||
=== Mutability
|
||||
## Variablen
|
||||
### Mutability
|
||||
|
||||
Standardmäßig sind Variablen nicht mutable, also nicht veränderbar.
|
||||
In anderen Sprachen ist das häufig `const` - in Rust gibt es aber auch `const`!
|
||||
|
||||
Das Folgende funktioniert also nicht:
|
||||
|
||||
[source.notCompiling, Rust]
|
||||
----
|
||||
<div class="notCompiling">
|
||||
```rust
|
||||
fn main() {
|
||||
let x = "Hello world!";
|
||||
// Das folgende funktioniert nicht, weil x nicht mutable ist!
|
||||
x = "Hello Rust!";
|
||||
}
|
||||
----
|
||||
```
|
||||
</div>
|
||||
|
||||
Damit Variablen mutable sind, muss `mut` genutzt werden:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
fn main() {
|
||||
let mut x = "Hello world!";
|
||||
// Hier funktioniert es.
|
||||
x = "Hello Rust!";
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
=== Constants
|
||||
### Constants
|
||||
|
||||
Neben unveränderlichen Variablen gibt es auch noch Konstanten.
|
||||
Diese sind sehr ähnlich zu ersteren, haben aber zwei relevante Unterschiede:
|
||||
@ -46,12 +50,11 @@ Die Konvention für Konstanten ist snake case all caps.
|
||||
|
||||
Ein Beispiel dafür ist folgendes:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
const MINUTES_IN_A_DAY: u32 = 24 * 60;
|
||||
----
|
||||
```
|
||||
|
||||
=== Shadowing
|
||||
### Shadowing
|
||||
|
||||
Shadowing wurde beim Higher-Lower-Game schon einmal erwähnt.
|
||||
Anfangs habe ich es falsch verstanden: Ich dachte Shadowing wäre, dass eine Variable unter dem selben Namen in unterschiedlichen Datentypen vorhanden wäre.
|
||||
@ -59,15 +62,14 @@ Anfangs habe ich es falsch verstanden: Ich dachte Shadowing wäre, dass eine Var
|
||||
Allerdings ist es mehr ein "Reuse" eines alten Namens.
|
||||
Ein Beispiel:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let x = x + 5;
|
||||
|
||||
println!("{}", x);
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
Die Ausgabe des Programms ist dabei der letztere Wert, hier also 10.
|
||||
Es ist also mehr eine neue Variable unter dem selben Namen wie die alte.
|
||||
@ -77,30 +79,29 @@ Da Variablen immer Block-Scope-basiert (?) sind, kann dies natürlich auch in ei
|
||||
|
||||
Der Unterschied zu mutable Variablen ist ganz einfach: neben einigen Unterschieden unter der Haube (oder?), haben mutable Variablen einen festen Datentyp, der nicht einfach geändert werden kann.
|
||||
|
||||
== Datentypen
|
||||
## Datentypen
|
||||
|
||||
=== Data Inference
|
||||
### Data Inference
|
||||
Jede Variable hat einen festen Datentyp.
|
||||
Der Compiler kann häufig selber herausfinden, was für einer das ist, das ist die "Type Inference".
|
||||
Wenn das nicht geht, muss manuell ein Typ festgelegt werden.
|
||||
|
||||
Ein Beispiel:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let guess: u32 = "42".parse().expect("Not a number!");
|
||||
----
|
||||
```
|
||||
|
||||
`"42"` ist offensichtlich ein String.
|
||||
`parse()` kann verschiedene Ergebnisse-Datentypen erzeugen.
|
||||
Das Ergebnis kann also verschiedene Typen haben, wir wollen ja aber wissen, was `guess` ist.
|
||||
Hier muss also `guess: u32` angegeben werden, sonst gibt es einen Fehler vom Compiler.
|
||||
|
||||
=== Scalar Types
|
||||
### Scalar Types
|
||||
Skalar heißt: ein einziges Value.
|
||||
Also eine Zahl (integer/float), Boolean oder ein einzelner Character.
|
||||
|
||||
==== Integer
|
||||
#### Integer
|
||||
Es signed und unsigned Integer und verschiedener Länge - 8, 16, 32, 64 und 128 Bit und "size".
|
||||
"size" ist dabei architektur-abhängig, also zumeist 32 oder 64 Bit.
|
||||
|
||||
@ -119,21 +120,20 @@ Interessant ist, dass es zusätzliche Methoden für alles gibt (nicht nur `add`)
|
||||
- `overflowing_add` gibt einen Boolean, ob ein Overflow auftritt
|
||||
- `saturating_add` bleibt beim Maximum oder Minimum des verfügbaren Bereiches
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let number: u8 = 254;
|
||||
println!("{}", number.wrapping_add(2));
|
||||
----
|
||||
```
|
||||
|
||||
Die Ausgabe des Programms ist 0.
|
||||
|
||||
==== Floats
|
||||
#### Floats
|
||||
Sind normale IEEE-754 floats mit 32 oder 64 Bit.
|
||||
|
||||
==== Boolean
|
||||
#### Boolean
|
||||
Auch nichts besonders, `true` oder `false` halt.
|
||||
|
||||
==== Chars
|
||||
#### Chars
|
||||
Sind besonders.
|
||||
Einzelne Character in Rust sind nicht einfach wie in C ein u8 unter anderem Namen, sondern wirklich ein Zeichen.
|
||||
Jeder Unicode-Character ist ein Char, also auch `'🐧'`.
|
||||
@ -141,18 +141,17 @@ Chars werden mit single-quotes geschrieben (Strings mit doppelten quotes).
|
||||
|
||||
Allerdings scheint es noch ein wenig komplizierter zu sein, das kommt aber erst später.
|
||||
|
||||
=== Compound Types
|
||||
### Compound Types
|
||||
Gruppierung von mehreren Werten in einem Typ.
|
||||
|
||||
==== Tupel
|
||||
#### Tupel
|
||||
Tupel sind weird.
|
||||
Sie haben eine feste Länge (wie C-Arrays), können aber verschiedene Datentypen beinhalten, also wie in Python.
|
||||
Sie sind aber schreibbar, wenn `mut` zur Initialisierung genutzt wird, also nicht wie in Python.
|
||||
|
||||
Ein paar Beispiele als Code:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let x: (f32, char, u8) = (1.0, '🐧', 3);
|
||||
//_x.0 = 2.0; // geht nicht, da x nicht mut ist.
|
||||
|
||||
@ -168,18 +167,17 @@ x.2 = 4; // x.2 ist schreibbar, wenn x mut ist.
|
||||
println!("{}", x.2);
|
||||
|
||||
//x.2 = 1.0; // Das geht nicht, da x.2 ein u8 ist.
|
||||
----
|
||||
```
|
||||
|
||||
Falls eine Funktion in Rust nichts zurückgibt, gibt sie in leeres Tupel `()`, auch `unit type` genannt, zurück.
|
||||
|
||||
==== Arrays
|
||||
#### Arrays
|
||||
Arrays sind wie C-Arrays, haben also eine feste Länge und nur einen Datentyp.
|
||||
Für "Arrays" mit veränderbarer Länge gibt es Vektoren.
|
||||
|
||||
Wieder etwas Code:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let x: [i32; 5] = [1, 2, 3, 4, 5];
|
||||
// ^ so sieht der Datentyp aus
|
||||
|
||||
@ -187,19 +185,18 @@ println!("{}", x[0]); // 1, so wie immer
|
||||
|
||||
let mut x = [15; 3]; // -> [15, 15, 15]
|
||||
x[0] = 16; // x = [16, 15, 15]
|
||||
----
|
||||
```
|
||||
|
||||
Im Gegensatz zu C-Arrays wird allerdings vor dem Zugriff auf das Array ein Check durchgeführt.
|
||||
Während C also auch außerhalb des Arrays Speicher lesen kann (mindestens theoretisch), kommt es in Rust dann zu einem Compilerfehler oder einer Runtime-Panic.
|
||||
|
||||
== Funktionen
|
||||
## Funktionen
|
||||
|
||||
Sind wie normale Funktionen in C auch. Keyword ist `fn`.
|
||||
|
||||
Beispiel:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
fn calculate_sum(a: i32, b: i32) -> i64 {
|
||||
// Statements können natürlich normal genutzt werden
|
||||
let c: i64 = a + b
|
||||
@ -210,22 +207,22 @@ fn calculate_sum(a: i32, b: i32) -> i64 {
|
||||
// Könnte aber auch einfach nur "a + b" sein.
|
||||
c
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
== Kommentare
|
||||
## Kommentare
|
||||
|
||||
Schon häufiger in den Beispielen - einfach `//`.
|
||||
Es gibt auch noch spezielle Docstrings, aber das kommt später.
|
||||
|
||||
== Kontrollfluss
|
||||
=== `if`
|
||||
## Kontrollfluss
|
||||
### `if`
|
||||
|
||||
- ohne runde Klammern um die Bedingung
|
||||
- _immer_ geschweifte Klammern, zumindest kein Beispiel ohne
|
||||
- Geht auch als short-if bei `let x = if condition { 5 } else { 6 }`
|
||||
- Bedingung *muss* ein bool sein!
|
||||
|
||||
=== `loop`
|
||||
### `loop`
|
||||
|
||||
- Basically ein `while (true)`
|
||||
- `break` und `continue`
|
||||
@ -233,8 +230,7 @@ Es gibt auch noch spezielle Docstrings, aber das kommt später.
|
||||
|
||||
Beispiel für labels:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
fn main() {
|
||||
'outer: loop {
|
||||
let mut a = 1;
|
||||
@ -246,15 +242,14 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
==== Ergebnis aus der Loop
|
||||
#### Ergebnis aus der Loop
|
||||
|
||||
`break` mit Wert ist Rückgabe.
|
||||
Einfaches Beispiel:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
fn main() {
|
||||
let mut counter = 0;
|
||||
|
||||
@ -268,19 +263,18 @@ fn main() {
|
||||
|
||||
println!("{}", result); // 20
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
=== `while`
|
||||
### `while`
|
||||
|
||||
- nutzt auch keine runden Klammern
|
||||
- sonst normal
|
||||
|
||||
=== `for`
|
||||
### `for`
|
||||
|
||||
Looped durch eine Collection (wie in Python).
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
|
||||
@ -288,4 +282,4 @@ fn main() {
|
||||
println!("{}", element);
|
||||
}
|
||||
}
|
||||
----
|
||||
```
|
@ -1,17 +1,21 @@
|
||||
:experimental:
|
||||
:docdatetime: 2022-10-18T17:56:26+02:00
|
||||
---
|
||||
title: Ownership
|
||||
published: 2022-10-18T17:56:26+02:00
|
||||
sorting: 4
|
||||
slug: ownership
|
||||
---
|
||||
|
||||
= Ownership
|
||||
# Ownership
|
||||
|
||||
https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html[Link zum Buch]
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html)
|
||||
|
||||
== Was ist das?
|
||||
## Was ist das?
|
||||
|
||||
Jeder Wert hat eine Variable, die ihn "besitzt".
|
||||
Jeder Wert kann zu einem Zeitpunkt nur von _einer_ Variable besessen werden.
|
||||
Sollte die Variable aus dem Scope verschwinden, wird der Wert ungültig und aus dem Speicher entfernt.
|
||||
|
||||
== Warum?
|
||||
## Warum?
|
||||
|
||||
Wenn ein Wert eine feste Länge hat, kann man sie ganz einfach auf den Stack packen.
|
||||
Falls die Länge aber variabel ist, muss zu Laufzeit Speicher allokiert werden.
|
||||
@ -23,9 +27,9 @@ Entweder kann Speicher zu früh (eventuell falsche Werte) oder zu spät (höhere
|
||||
|
||||
Rust nutzt deshalb (wenn man das nicht aktiv anders macht) einen anderen Ansatz, bei dem der Compiler selber `drop` (was in etwa `free` entspricht) einfügt, wenn eine Variable aus dem Scope verschwindet.
|
||||
|
||||
== Was das für den Code bedeutet
|
||||
## Was das für den Code bedeutet
|
||||
|
||||
=== String Datentyp
|
||||
### String Datentyp
|
||||
|
||||
Fangen wir mal mit einem Datentypen an, den das betrifft.
|
||||
|
||||
@ -35,27 +39,25 @@ Dieser String-Typ hat den Vorteil, dass er eine dynamische Länge hat und damit
|
||||
|
||||
Ein Beispiel:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let mut x = String::from("Hello"); // Legt "dynamischen" String an
|
||||
x.push_str(" world!"); // Konkatiniert an den String
|
||||
|
||||
println!("{}", x); // "Hello world!"
|
||||
----
|
||||
```
|
||||
|
||||
Das geht mit den normalen String-Literalen (`let mut x = "Hello";`) nicht, da diese eine immer eine feste Länge haben.
|
||||
Theoretisch kann `x` natürlich dann überschrieben werden, mit einem String anderer Länge, aber anscheinend wird das von Rust überdeckt und wahrscheinlich ähnlich wie Shadowing gehandhabt.
|
||||
|
||||
=== Move
|
||||
### Move
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let x = 5; // Int -> feste Größe und auf Stack
|
||||
let y = x;
|
||||
|
||||
let s1 = String::from("Hello world"); // Dynamischer String auf Heap
|
||||
let s2 = s1;
|
||||
----
|
||||
```
|
||||
|
||||
Hier trifft ähnliches zu, wie zum Beispiel in Python: primitive Datentypen, wie `int` oder `float`, werden einfach kopiert, wenn sie einer anderen Variable zugewiesen werden.
|
||||
Bei Objekten auf dem Heap dagegen, wird auch kopiert, allerdings nur was wirklich in `s1` steht: die Referenz auf den Speicher (also ein Pointer), die Länge und andere "Metadaten".
|
||||
@ -74,7 +76,7 @@ Die Methode dafür (muss natürlich implementiert sein) heißt `clone`.
|
||||
Wir könnten also auch schreiben `let s2 = s1.clone()` und beide Variablen wären unabhängig voneinander und gültig.
|
||||
Das kann aber sehr teuer für die Laufzeit sein!
|
||||
|
||||
=== Copy und Drop Annotation
|
||||
### Copy und Drop Annotation
|
||||
|
||||
Im Buch wird jetzt noch kurz angeschnitten, dass diese primitiven Datentypen kopiert werden, weil sie das `Copy` "trait" implementiert hätten.
|
||||
An dem Punkt habe ich noch keine Ahnung, was das ist, aber ich denke es wird so ähnlich sein, wie Java Interfaces?
|
||||
@ -83,7 +85,7 @@ Wenn ein Datentyp den `Copy` trait hat, wird es auf jeden Fall einfach kopiert,
|
||||
|
||||
Es gibt auch ein `Drop` trait, mit dem noch irgendwas ausgeführt werden kann, wenn ein Wert dieses Types gedropped wird. Dieser trait ist exklusiv zu `Copy`.
|
||||
|
||||
== In Funktionen
|
||||
## In Funktionen
|
||||
|
||||
Sollte eine Funktion eine Variable übergeben bekommen, wird auch das Ownership der Variable dahin übergeben.
|
||||
Nach Ausführen der Funktion ist die Variable ungültig.
|
||||
@ -108,15 +110,16 @@ Das heißt natürlich auch, dass alle immutable Referenzen invalid werden, sobal
|
||||
|
||||
Damit werden (unter anderem) Race Conditions schon beim Compilen verhindert.
|
||||
|
||||
=== Dangling references
|
||||
### Dangling references
|
||||
|
||||
[source.notCompiling, rust]
|
||||
----
|
||||
<div class="notCompiling">
|
||||
```rust
|
||||
fn dangle() -> &String {
|
||||
let s = String::from("hello");
|
||||
&s // Referenz auf s returned
|
||||
} // Hier fliegt s aus dem Scope
|
||||
----
|
||||
```
|
||||
</div>
|
||||
|
||||
Hier ist eine Funktion gebaut, die nur eine Referenz zurückgibt.
|
||||
Allerdings wird `s` ja (da nach Funktion out of scope) nach der Funktion gedropped.
|
||||
@ -125,7 +128,7 @@ Der Compiler gibt uns dafür auch einen Fehler.
|
||||
Das Tutorial sagt an diesem Punkt, dass man am besten keine Referenzen zurückgibt, die Fehlermeldung redet aber auch noch von "lifetimes" und dass `&'static String` ein möglicher Rückgabetyp wäre.
|
||||
Das kommt wohl aber erst später...
|
||||
|
||||
== Der Slice-Datentyp
|
||||
## Der Slice-Datentyp
|
||||
|
||||
Wenn wir auf Arrays arbeiten, wäre es ja cool, an verschiedenen Stellen gleichzeitig zu arbeiten.
|
||||
Nur so kann multithreading etc. funktionieren.
|
||||
@ -133,13 +136,12 @@ Nur so kann multithreading etc. funktionieren.
|
||||
Dafür hat Rust den Slice-Datentyp.
|
||||
Der funktioniert ähnlich wie Array-Ranges in Python.
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let s = String::from("hello world");
|
||||
|
||||
let hello = &s[0..5];
|
||||
let world = &s[6..11];
|
||||
----
|
||||
```
|
||||
|
||||
Rust kümmert sich dabei darum, dass wir jetzt keinen Unsinn mehr mit `s` machen.
|
||||
Sollte man versuchen `s` zu mutaten und danach die Slice zu nutzen, gibt es einen Fehler, denn Slices sind genauso Referenzen.
|
@ -1,11 +1,15 @@
|
||||
:experimental:
|
||||
:docdatetime: 2022-08-10T17:04:53+02:00
|
||||
---
|
||||
title: Structs
|
||||
published: 2022-08-10T17:04:53+02:00
|
||||
sorting: 5
|
||||
slug: structs
|
||||
---
|
||||
|
||||
= Structs
|
||||
# Structs
|
||||
|
||||
https://doc.rust-lang.org/book/ch05-00-structs.html[Link zum Buch]
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch05-00-structs.html)
|
||||
|
||||
== Was sind Structs
|
||||
## Was sind Structs
|
||||
|
||||
Structs kennt man ja aus C/C++.
|
||||
Man kann es (denke ich) auch mit JavaScript Objekten vergleichen.
|
||||
@ -13,12 +17,11 @@ Man kann es (denke ich) auch mit JavaScript Objekten vergleichen.
|
||||
In Structs gruppiert man zusammengehöriges Zeug und hat so eine Art Pseudo-OOP.
|
||||
Man kann damit neue Datentypen machen.
|
||||
|
||||
== How to
|
||||
## How to
|
||||
|
||||
=== "Normale" Structs
|
||||
### "Normale" Structs
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
struct User {
|
||||
active: bool,
|
||||
username: String,
|
||||
@ -38,59 +41,56 @@ fn main() {
|
||||
|
||||
user1.email = String::from("anotheremail@example.com");
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
Hinweis: Es können nicht einzelne Felder mutable sein, sondern wenn dann immer das ganze Struct.
|
||||
|
||||
==== Dinge wie in Javascript
|
||||
#### Dinge wie in Javascript
|
||||
Wenn die Variable heißt wie das Feld, kann man auch statt `email: email` einfach nur `email` schreiben.
|
||||
|
||||
Wenn man ein neues Struct aus einem alten mit Updates erstellen will, geht das auch mit einer Art Spread-Parameter:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let user2 = User {
|
||||
email: String::from("another@example.com"),
|
||||
..user1
|
||||
};
|
||||
----
|
||||
```
|
||||
|
||||
`..user1` *muss* als letztes kommen und füllt dann alle bisher nicht gesetzten Felder.
|
||||
Außerdem ist das etwas tricky:
|
||||
Wenn die Daten, die von `user1` zu `user2` übertragen werden, gemoved werden (sprich: keine primitiven Datentypen sind), dann ist `user1` danach ungültig.
|
||||
Hätten wir jetzt auch noch einen neuen `username` gesetzt (auch ein String) und nur `active` und `sign_in_count` übertragen, wäre `user1` noch gültig.
|
||||
|
||||
=== Tupel Structs
|
||||
### Tupel Structs
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
struct RGBColor(u8, u8, u8);
|
||||
|
||||
fn main() {
|
||||
let black = Color(0, 0, 0)
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
Sind nutzbar wie Tupel (destrucuture und `.index` zum Zugriff auf Werte), allerdings eben ein eigener Typ.
|
||||
|
||||
=== Unit-Like Structs
|
||||
### Unit-Like Structs
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
struct AlwaysEqual;
|
||||
----
|
||||
```
|
||||
|
||||
Ein Struct muss keine Felder haben.
|
||||
Das Buch meint, man könnte für diesen Datentypen jetzt noch Traits implementieren, aber davon habe ich noch keine Ahnung.
|
||||
Nur dann macht diese Art von Struct irgendwie Sinn.
|
||||
|
||||
== Ownership der Felder
|
||||
## Ownership der Felder
|
||||
|
||||
Im ersten Beispiel wird `String` satt `&str` genutzt.
|
||||
Wir wollen am besten im Struct keine Referenzen, oder es müssen "named lifetime parameter" sein, etwas das wir erst später lernen.
|
||||
Der Compiler wird sonst streiken.
|
||||
|
||||
== Das erste Mal Traits
|
||||
## Das erste Mal Traits
|
||||
|
||||
Im Buch folgt ein Beispielprogramm für ein Struct, das ein Rechteck abbildet.
|
||||
Wir wollten das ganze printen (mit `{}` als Platzhalter), allerdings implementiert Das Rechteck nicht `std::fmt::Display`.
|
||||
@ -112,15 +112,14 @@ Alternativ kann man auch das Makro `dbg!(...)` nutzen.
|
||||
Das wird dann auf `stderr` geprintet.
|
||||
Man kann sogar ein Statement da rein packen (also zum Beispiel `30 * x`) und bekommt das Statement mit dem Ergebnis geprintet, wobei das Ergebnis (als Wert, nicht Referenz) auch zurückgegeben wird.
|
||||
|
||||
== Funktionen in Structs
|
||||
## Funktionen in Structs
|
||||
|
||||
Unser Struct soll jetzt auch eine Funktion auf sich selbst aufrufen können.
|
||||
Tatsächlich ist der sehr einfach und sehr OOPig.
|
||||
Tatsächlich ist der sehr einfach und sehr OOP-ig.
|
||||
|
||||
Die folgenden Beispiele sollten relativ viel erklären:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
@ -177,15 +176,15 @@ fn main() {
|
||||
println!("{}", rect2.has_same_area(&rect1)); // true
|
||||
println!("{}", Rectangle::same_area(&rect1, &rect2)); // true
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
=== `&mut self`
|
||||
### `&mut self`
|
||||
|
||||
Eine Methode kann auch `&mut self` als ersten Parameter haben.
|
||||
Dann können auch Felder geschrieben werden. In diesem Fall werden Referenzen aber invalidiert!
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
<div class="notCompiling">
|
||||
```rust
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
@ -207,4 +206,5 @@ fn main() {
|
||||
|
||||
println!("{}", ref1.width); // <- geht nicht!
|
||||
}
|
||||
----
|
||||
```
|
||||
</div>
|
@ -1,11 +1,15 @@
|
||||
:experimental:
|
||||
:docdatetime: 2022-10-18T17:56:26+02:00
|
||||
---
|
||||
title: Enums & Matching
|
||||
published: 2022-10-18T17:56:26+02:00
|
||||
sorting: 6
|
||||
slug: enums
|
||||
---
|
||||
|
||||
= Enums und Pattern Matching
|
||||
# Enums und Pattern Matching
|
||||
|
||||
https://doc.rust-lang.org/book/ch06-00-enums.html[Link zum Buch]
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch06-00-enums.html)
|
||||
|
||||
== Enums
|
||||
## Enums
|
||||
|
||||
Enumarations gibt's in vielen Programmiersprachen, in Rust scheinen sie aber eine große Rolle einzunehmen.
|
||||
"Enumeration" stimmt eigentlich gar nicht, Enums haben hier nämlich nicht zwangsläufig was mit Zahlen zu tun.
|
||||
@ -14,15 +18,14 @@ Grundsätzlich ist ein "Enum" in Rust näher am "Union" würde ich denken.
|
||||
Ein einfaches Beispiel für ist der Typ `Option<T>` (vergleichbar mit Python oder Java `Optional`).
|
||||
Dieser ist entweder `None` oder `Some(value: T)` - es kann also ein Wert zusätzlich zur "Definition" beinhalten.
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
enum Farbcode {
|
||||
Hex,
|
||||
Rgb,
|
||||
}
|
||||
|
||||
let hexcolor = Farbcode::Hex;
|
||||
----
|
||||
```
|
||||
|
||||
`Farbcode` ist also ein im Code benutzbarer Datentyp, genauso wie `Farbcode::Hex`.
|
||||
Wenn eine Funktion nun eine Variable mit Typ `Farbcode` erwartet, kann diese Variable sowohl `Hex` oder `Rgb` sein.
|
||||
@ -30,8 +33,7 @@ Die Funktion kann dann je nach Typ verschieden funktionieren.
|
||||
|
||||
Wie schon erwähnt, kann so ein Enum-Wert auch Werte beinhalten, um das zu machen, schreiben wir den Code einfach um:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
enum Farbcode {
|
||||
Hex(String),
|
||||
Rgb(u8, u8, u8),
|
||||
@ -48,14 +50,13 @@ enum Farbcode {
|
||||
|
||||
let hexcode = Farbcode::Hex(String::from("00affe"));
|
||||
let rgbcode = Farbcode::Rgb(125, 255, 255);
|
||||
----
|
||||
```
|
||||
|
||||
Natürlich können die Structs jeder Art sein.
|
||||
Enums sind aber auch selber eine Art Struct.
|
||||
Also können wir für Enums auch Methoden definieren wie für Structs.
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
impl Farbcode {
|
||||
fn to_css_string(&self) {
|
||||
// Methode, die für Hex und Rgb angewendet werden kann
|
||||
@ -64,18 +65,18 @@ impl Farbcode {
|
||||
|
||||
let rgbcode = Farbcode::Rgb(125, 255, 255);
|
||||
rgbcode.to_css_string();
|
||||
----
|
||||
```
|
||||
|
||||
Tatsächlich ist damit so etwas wie Vererbung implementierbar.
|
||||
Es gibt zwar keine Attribute, aber da ja auch die internen Structs Methoden haben können, ist eine gewisse Hierarchie erstellbar.
|
||||
|
||||
=== `Option<T>`
|
||||
### `Option<T>`
|
||||
|
||||
Options hab ich oben schonmal kurz beschrieben.
|
||||
In Rust ist dieser Datentyp sehr wichtig.
|
||||
Die Dokumentation dazu ist https://doc.rust-lang.org/std/option/enum.Option.html[hier zu finden] und enthält sehr viel Wichtiges und Interessantes.
|
||||
Die Dokumentation dazu ist [hier zu finden](https://doc.rust-lang.org/std/option/enum.Option.html) und enthält sehr viel Wichtiges und Interessantes.
|
||||
|
||||
== `match`
|
||||
## `match`
|
||||
|
||||
`match` ist quasi das `switch` von Rust.
|
||||
Nur kann es auch prüfen, ob eine Variable einem Enum-Typen angehört.
|
||||
@ -84,8 +85,7 @@ So wie Rust bis jetzt klang, kann wahrscheinlich jedem Datentypen ein "match-Tra
|
||||
Aber ganz einfach: Angenommen wir wollen die Methode `to_css_string` von oben implementieren.
|
||||
Diese Methode muss ja, je nach Typ, völlig unterschiedlich funktionieren.
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
enum Farbcode {
|
||||
Hex(String),
|
||||
Rgb(u8, u8, u8),
|
||||
@ -108,26 +108,25 @@ fn main() {
|
||||
println!("{}", hexcode.to_css_string());
|
||||
println!("{}", rgbcode.to_css_string());
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
Hier sieht man auch ganz gut, wie im Match dem "Inhalt" des Enums direkt Namen gegeben werden und Tuples auch dekonstruiert.
|
||||
Im Beispiel ist auch deutlich, dass `match` einen Rückgabewert hat, nämlich das, was im Statement(-Block) des jeweiligen Matches zurückgegeben wird.
|
||||
|
||||
=== Vollständigkeit
|
||||
### Vollständigkeit
|
||||
|
||||
Entweder muss ein `match` eines Enums jede mögliche Variante abgrasen oder es gibt zwei Alternativen.
|
||||
|
||||
`other` ist quasi das `default` von Rust.
|
||||
Aber auch `\_` matched alles.
|
||||
Aber auch `_` matched alles.
|
||||
Der Unterschied ist, dass bei `other` noch der Inhalt genutzt werden kann, bei `_` wird er direkt ignoriert und ist nicht nutzbar.
|
||||
|
||||
=== `if let`
|
||||
### `if let`
|
||||
|
||||
Dieses if-Konstrukt nutzt man am besten, wenn man nur auf eine einzelne Variante eines Enums prüfen möchte.
|
||||
Letztendlich ist es ganz simpel:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
enum Muenzwurf {
|
||||
Kopf,
|
||||
@ -149,4 +148,4 @@ fn main() {
|
||||
let ergebnis = Muenzwurf::Seite;
|
||||
print_wurf(ergebnis); // Das glaub ich nicht! Seite?!
|
||||
}
|
||||
----
|
||||
```
|
@ -1,17 +1,21 @@
|
||||
:experimental:
|
||||
:docdatetime: 2022-10-18T17:56:26+02:00
|
||||
---
|
||||
title: Projektmanagement
|
||||
published: 2022-10-18T17:56:26+02:00
|
||||
sorting: 7
|
||||
slug: projektmanagement
|
||||
---
|
||||
|
||||
= How to: Projektmanagement
|
||||
# How to: Projektmanagement
|
||||
|
||||
https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html[Link zum Buch]
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html)
|
||||
|
||||
== Packages, Crates, Modules, was?
|
||||
## Packages, Crates, Modules, was?
|
||||
|
||||
Rust hat ein sehr hierarchisches Konzept, was die Strukturierung von Projekten angeht.
|
||||
|
||||
Fangen wir mal von oben an:
|
||||
|
||||
=== Packages
|
||||
### Packages
|
||||
|
||||
Packages bestehen aus Crates.
|
||||
Sie fassen diese also quasi zusammen und in `Cargo.toml` wird definiert, wie die Crates zu bauen sind.
|
||||
@ -20,29 +24,29 @@ Jedes Package, das wir bis jetzt erstellt haben, hatte standardmäßig eine "bin
|
||||
|
||||
Die Crates können (soweit wie ich das verstanden habe) in beliebigen Ordnern existieren, falls die Crate so heißen soll wie das Package, ist der Standardpfad `src/main.rs` (für binary) bzw. `src/lib.rs` (für library).
|
||||
|
||||
==== Warum mehrere Crates in einem Projekt?
|
||||
#### Warum mehrere Crates in einem Projekt?
|
||||
|
||||
Einfaches Beispiel: Man hat eine library crate, die Funktionen für einen Webserver bereitstellt.
|
||||
Man kann dann einfach eine binary crate hinzufügen, die eine Referenz-Nutzung abbildet, also direkt ein Beispiel ist.
|
||||
Dies hilft Nutzern direkt und gleichzeitig testet es direkt auch (wobei richtige Tests natürlich anders zu implementieren sind).
|
||||
|
||||
=== Crates
|
||||
### Crates
|
||||
|
||||
Creates sind die eigentlichen "Module".
|
||||
Es gibt zwei Arten: binary und library.
|
||||
|
||||
==== Binary Crates
|
||||
#### Binary Crates
|
||||
|
||||
Diese Crates können zu einer ausführbaren Datei kompiliert werden.
|
||||
Jedes der bisherigen Beispiele, z.B. auch das link:#/diary/rust/3[Higher-Lower-Spiel] sind eine solche binary crate.
|
||||
Jedes der bisherigen Beispiele, z.B. auch das [Higher-Lower-Spiel](/blog/rust/higher-lower-game) sind eine solche binary crate.
|
||||
|
||||
Ihr Merkmal ist vor allem, dass eine `main`-Funktion existiert, die der Einstiegspunkt ist.
|
||||
|
||||
==== Library Crate
|
||||
#### Library Crate
|
||||
|
||||
Wie der Name schon sagt, stellt diese Art Crate nur Funktionen zur Verfügung wie eine Bibliothek.
|
||||
|
||||
=== Modules
|
||||
### Modules
|
||||
|
||||
Innerhalb einer Crate können Module existieren.
|
||||
Und hier ist auch schon wieder von OOP abgeschaut.
|
||||
@ -53,8 +57,8 @@ Im Hauptprogramm kann mit `mod modulname;` das Modul eingebunden werden. Gesucht
|
||||
Zusätzlich kann auch direkt inline ein Modul erstellt werden.
|
||||
Ein Beispiel:
|
||||
|
||||
[source.notCompiling, rust]
|
||||
----
|
||||
<div class="notCompiling">
|
||||
```rust
|
||||
mod testmodul {
|
||||
mod nested_modul {
|
||||
fn funktion() {
|
||||
@ -74,14 +78,14 @@ fn main() {
|
||||
// Hello world! Geht nicht...
|
||||
crate::testmodul::nested_modul::funktion();
|
||||
}
|
||||
----
|
||||
```
|
||||
</div>
|
||||
|
||||
Das funktioniert noch *nicht*.
|
||||
Denn standardmäßig ist alles private, was nicht explizit public ist.
|
||||
Damit wir den obigen Aufruf machen können, muss der Code so aussehen:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
mod testmodul {
|
||||
pub mod nested_modul {
|
||||
pub fn funktion() {
|
||||
@ -101,24 +105,23 @@ fn main() {
|
||||
// Hello world!
|
||||
crate::testmodul::nested_modul::funktion();
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
Nur so kann auf Submodule und Funktionen dieser Module zugegriffen werden.
|
||||
Wie im "normalen" OOP, können aus diesen öffentlichen Funktionen aber dann auch private aufgerufen werden.
|
||||
|
||||
==== Von unten nach oben
|
||||
#### Von unten nach oben
|
||||
|
||||
Um aus einem inneren Modul auf das äußere zuzugreifen, kann übrigens `super::...` verwendet werden.
|
||||
|
||||
==== Structs und Enums
|
||||
#### Structs und Enums
|
||||
|
||||
In Modulen können natürlich auch Structs und Enums verwendet werden.
|
||||
|
||||
Bei Structs ist die Besonderheit, dass die einzelnen Attribute auch wieder private oder public sein können.
|
||||
So kann man folgendes machen:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
mod testmodul {
|
||||
pub struct Teststruct {
|
||||
pub oeffentlich: String,
|
||||
@ -142,11 +145,11 @@ fn main() {
|
||||
// Geht nicht!
|
||||
// println!("Privat: {}", a.privat);
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
Dagegen gilt für Enums: Wenn der Enum public ist, sind auch alle Varianten public.
|
||||
|
||||
==== Abkürzungen mit `use`
|
||||
#### Abkürzungen mit `use`
|
||||
|
||||
Angenommen, wir haben eine Mediathek mit Filmen, Serien, Spielen, etc. und brauchen immer lange Zugriffspfade (also z.B. `crate::medien::spiele::liste::add()`), obwohl wir nur Spiele brauchen, kann `use` benutzt werden.
|
||||
|
@ -1,11 +1,15 @@
|
||||
:experimental:
|
||||
:docdatetime: 2022-10-18T17:56:27+02:00
|
||||
---
|
||||
title: Collections
|
||||
published: 2022-10-18T17:56:27+02:00
|
||||
sorting: 8
|
||||
slug: collections
|
||||
---
|
||||
|
||||
= Standard Collections
|
||||
# Standard Collections
|
||||
|
||||
https://doc.rust-lang.org/book/ch08-00-common-collections.html[Link zum Buch]
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch08-00-common-collections.html)
|
||||
|
||||
== `Vec<T>` - Vektoren
|
||||
## `Vec<T>` - Vektoren
|
||||
|
||||
Vektoren kennt man ja aus C++ als dynamische Alternative zu Arrays.
|
||||
Es ist quasi eine Linked List, die beliebig erweiterbar bzw. manipulierbar ist.
|
||||
@ -14,8 +18,7 @@ Wie in der Überschrift zu sehen, sind sie typspezifisch, man kann also nur Date
|
||||
Wie benutze ich jetzt so einen Vector?
|
||||
Hier einfach mal eine Übersicht:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
// -- Erstellen --
|
||||
// Mit dem vec!-Pragma
|
||||
let v = vec![1, 2, 3];
|
||||
@ -56,35 +59,35 @@ match v.get(2) {
|
||||
for i in &mut v {
|
||||
*i += 50;
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
=== Achtung, Scope
|
||||
### Achtung, Scope
|
||||
|
||||
Wenn ein Vector aus dem Scope fällt, wird er zusammen mit seinem Inhalt gedropped.
|
||||
Blöd, wenn man Referenzen auf Elemente aus dem Vector hat.
|
||||
|
||||
=== Ownership
|
||||
### Ownership
|
||||
|
||||
Wenn `push()` ausgeführt wird, findet ein mutable borrow statt und das kommt mit allen Eigenheiten wie vorher.
|
||||
Alle Referenzen, die vorher über Index oder `get()` genommen wurden, sind dann ungültig.
|
||||
Das liegt daran, dass es by `push()` passieren kann, dass neue Speicher reserviert und genutzt werden muss, falls die Elemente nicht mehr nebeneinander passen.
|
||||
|
||||
=== Lifehack: Enum für verschiedene Datentypen
|
||||
### Lifehack: Enum für verschiedene Datentypen
|
||||
|
||||
Ein Vector kann nur einen Datentypen aufnehmen?
|
||||
Der Datentyp kann aber auch ein Enum sein!
|
||||
|
||||
Also wenn mal ein String neben Zahlen gespeichert werden soll: Einfach einen Enum mit beiden Varianten anlegen.
|
||||
|
||||
=== Weiteres
|
||||
### Weiteres
|
||||
|
||||
Es gibt auch hier Slices und noch eine Menge Tricks.
|
||||
Die https://doc.rust-lang.org/std/vec/struct.Vec.html[Dokumentation zum Vector] ist da wahrscheinlich sehr hilfreich.
|
||||
Die [Dokumentation zum Vector](https://doc.rust-lang.org/std/vec/struct.Vec.html) ist da wahrscheinlich sehr hilfreich.
|
||||
|
||||
== Strings
|
||||
## Strings
|
||||
|
||||
Strings eine Collection?
|
||||
Klar, wie in C ja auch.
|
||||
Klar, wie in C oder Python ja auch.
|
||||
|
||||
Es gibt im Core eigentlich nur `str`, also ein Slice.
|
||||
Der `String`-Typ kommt aus der Standard-Lib und ist einfacher zu nutzen.
|
||||
@ -96,13 +99,12 @@ Natürlich funktioniert auch `String::from("text")`.
|
||||
String sind UTF-8 encoded, also egal mit was man sie bewirft, es sollte klappen.
|
||||
Allerdings ist das Handling deshalb etwas kompliziert.
|
||||
Rust fasst das ganz gut am Ende der Seite zusammen mit
|
||||
[quote]
|
||||
To summarize, strings are complicated.
|
||||
|
||||
> To summarize, strings are complicated.
|
||||
|
||||
Hier wieder eine Übersicht zur Nutzung:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
// -- Erstellen --
|
||||
// String::from()
|
||||
// "Hello ".to_string() macht das selbe
|
||||
@ -131,27 +133,26 @@ let s2 = String::from("Stein");
|
||||
let s3 = String::from("Papier");
|
||||
let s4 = format!("{}, {}, {}", s1, s2, s3);
|
||||
// Hier wird kein Ownership übergeben!
|
||||
----
|
||||
```
|
||||
|
||||
=== Indexing
|
||||
### Indexing
|
||||
|
||||
Aus Python z.B. kennt man ja `"Hallo"[0] -> H`.
|
||||
In Rust geht das nicht.
|
||||
Das liegt am Aufbau der String, dass sie eben UTF-8 verwenden und `String` eigentlich nur ein `Vec<u8>` ist.
|
||||
Das macht das ganze ordentlich schwierig.
|
||||
|
||||
=== Slicing
|
||||
### Slicing
|
||||
|
||||
Ist immer eine schlechte Idee, außer man weiß exakt wie lang die einzelnen Zeichen (in Byte) des Strings sind.
|
||||
Im Englischen ist es normalerweise 1 Byte pro Zeichen, Umlaute sind schon 2, und so weiter.
|
||||
Sollte man aus Versehen ein Zeichen "durchschneiden" (also nur 1 Byte eines "ü" im Slice haben), gibt es eine Runtime Panic.
|
||||
|
||||
=== Iterieren
|
||||
### Iterieren
|
||||
|
||||
Über einem String iterieren geht ganz ok.
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
for c in "hallo".chars() {
|
||||
println!("{}", c);
|
||||
}
|
||||
@ -162,19 +163,18 @@ for b in "hallo".bytes() {
|
||||
println!("{}", b);
|
||||
}
|
||||
// Wirft eben die einzelnen u8 raus.
|
||||
----
|
||||
```
|
||||
|
||||
Wenn wir "grapheme" haben wollen (Was anscheinend so etwas wie "volle Zeichen" sind, mehr als nur char), gibt es keine eingebaute Funktion aber crates, die das lösen.
|
||||
|
||||
== HashMaps
|
||||
## HashMaps
|
||||
|
||||
Der Erlöser der Programmierer und Lösung jeder Aufgabe bei der Bewerbung, die "O(n)" enthält.
|
||||
Oder so ähnlich.
|
||||
|
||||
Nutzung:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
// Das hier ist für die "Abkürzungen"
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -210,13 +210,13 @@ for (key, value) in &zahlwort {
|
||||
println!("{}: {}", key, value);
|
||||
}
|
||||
// Sehr selbsterklärend
|
||||
----
|
||||
```
|
||||
|
||||
=== Ownership
|
||||
### Ownership
|
||||
|
||||
Falls Key oder Value kein Copy Trait haben, wird der Ownership übergeben. Strings sind also danach ungültig.
|
||||
|
||||
== Hausaufgaben
|
||||
## Hausaufgaben
|
||||
|
||||
Das Buch gibt uns hier ein paar Aufgaben, die wir jetzt lösen können:
|
||||
|
||||
@ -226,14 +226,13 @@ Das Buch gibt uns hier ein paar Aufgaben, die wir jetzt lösen können:
|
||||
|
||||
Vielleicht werde ich sie irgendwann mal lösen, dann landet der Code hier.
|
||||
|
||||
=== Aufgabe 1
|
||||
### Aufgabe 1
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
fn main() {
|
||||
let mut list = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
list.sort();
|
||||
let mid = list.len() / 2; // integer divide
|
||||
println!("{}", list[mid]);
|
||||
}
|
||||
----
|
||||
```
|
@ -1,11 +1,15 @@
|
||||
:experimental:
|
||||
:docdatetime: 2022-08-22T17:04:01+02:00
|
||||
---
|
||||
title: Fehler und PANIK!
|
||||
published: 2022-08-22T17:04:01+02:00
|
||||
sorting: 9
|
||||
slug: errors-and-panic
|
||||
---
|
||||
|
||||
= Errors und `panic!`
|
||||
# Errors und `panic!`
|
||||
|
||||
https://doc.rust-lang.org/book/ch09-00-error-handling.html[Link zum Buch]
|
||||
[Link zum Buch](https://doc.rust-lang.org/book/ch09-00-error-handling.html)
|
||||
|
||||
== `panic!`
|
||||
## `panic!`
|
||||
|
||||
Dieses Makro it furchtbar simpel: Es macht Panik und das Programm stirbt mit einem Fehler.
|
||||
Diesen Fehler kann man auch nicht catchen.
|
||||
@ -14,13 +18,12 @@ Wenn `RUST_BACKTRACE` als Umgebungsvariable gesetzt ist, wird auch noch ein lang
|
||||
|
||||
Will man gar kein Traceback und kein "unwinding" (das "hochgehen" durch den Funktionsstack und Aufräumen), kann man auch noch folgendes zu seiner `Cargo.toml` hinzufügen:
|
||||
|
||||
[source, toml]
|
||||
----
|
||||
```toml
|
||||
[profile.release]
|
||||
panic = 'abort'
|
||||
----
|
||||
```
|
||||
|
||||
== `Result<T, E>`
|
||||
## `Result<T, E>`
|
||||
|
||||
Der Result-Datentyp ist deutlich besser für mögliche Fehler geeignet, die das Programm abfangen und bearbeiten kann.
|
||||
Falls zum Beispiel eine Datei auf dem Dateisystem nicht existiert, ist es ja manchmal gewünscht, dass diese Datei dann einfach angelegt wird.
|
||||
@ -36,8 +39,7 @@ Der genaue Fehler kann mit `error.kind()` erfahren werden; ein weiteres `match`
|
||||
|
||||
Ein volles Beispiel mit ganz viel `match`:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
use std::fs::File;
|
||||
use std::io::ErrorKind;
|
||||
|
||||
@ -57,23 +59,22 @@ fn main() {
|
||||
},
|
||||
};
|
||||
}
|
||||
----
|
||||
```
|
||||
|
||||
=== `unwrap()` und `expect()`
|
||||
### `unwrap()` und `expect()`
|
||||
|
||||
Machen aus einem `Result<T, E>` entweder ein `T` oder eine `panic!`.
|
||||
Bei `expect()` kann man noch die Fehlermeldung festlegen.
|
||||
|
||||
Warum man jemals `unwrap()` nehmen sollte, erschließt sich mir nicht ganz.
|
||||
|
||||
=== `?`
|
||||
### `?`
|
||||
|
||||
Oft schreibt man Funktionen so, dass Fehler weiter "hochgegeben" werden, falls man welche bekommt.
|
||||
`?` macht genau das bei einem Result.
|
||||
Codemäßig erklärt:
|
||||
|
||||
[source, rust]
|
||||
----
|
||||
```rust
|
||||
let a = match result {
|
||||
Ok(nummer) => nummer,
|
||||
Err(e) => return Err(e),
|
||||
@ -82,11 +83,11 @@ let a = match result {
|
||||
// Ergibt das selbe wie
|
||||
|
||||
let a = result?;
|
||||
----
|
||||
```
|
||||
|
||||
Das `?` kann auch für zum Beispiel `Option` verwendet werden, dann returned es natürlich `None`.
|
||||
|
||||
=== Rückgaben von `main()`
|
||||
### Rückgaben von `main()`
|
||||
|
||||
Bis jetzt hat `main()` immer nichts, also implizit `()` zurückgegeben.
|
||||
Manchmal wollen wir ja aber auch was anderes als "0" als return code haben.
|
||||
@ -95,7 +96,7 @@ Der zweite Typ dort, kann wohl als "irgendein Fehler" gelesen werden und wird sp
|
||||
|
||||
Allgemein kann aber jedes Objekt, dass `std::process::Termination`-Trait implementiert von main als Rückgabe genutzt werden.
|
||||
|
||||
== Wann `Result<T, E>`, wann `panic!`?
|
||||
## Wann `Result<T, E>`, wann `panic!`?
|
||||
|
||||
Der Artikel ist sehr sehr sehr lang, aber eigentlich sagt er:
|
||||
"Panic nur wenn es eben nicht gerettet werden kann."
|
@ -109,6 +109,17 @@ const { title } = Astro.props;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-left: 3px solid var(--blog_content-border);
|
||||
padding: .7em 1em;
|
||||
margin-left: 1em;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* A custom indicator that this code is in fact faulty */
|
||||
.notCompiling code {
|
||||
display: block;
|
||||
|
Loading…
Reference in New Issue
Block a user