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
|
## 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.
|
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:
|
Output:
|
||||||
```
|
> Hello world!
|
||||||
Hello world!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Komplettes Programm
|
## 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
|
||||||
|
|
||||||
|
```rust
|
||||||
~*In[2]:*~
|
use std::io;
|
||||||
[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!("Guess the number!");
|
||||||
|
|
||||||
println!("Please input your guess.");
|
println!("Please input your guess.");
|
||||||
|
|
||||||
let mut guess = evcxr_input::get_string("Number? ");
|
let mut guess = String::new();
|
||||||
// Das ^ ist für Jupyter
|
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
||||||
// 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);
|
println!("You guessed: {}", guess);
|
||||||
----
|
```
|
||||||
|
|
||||||
|
Ausgabe:
|
||||||
|
> Guess the number!
|
||||||
|
> Please input your guess.
|
||||||
|
> Number? 42
|
||||||
|
> You guessed: 42
|
||||||
|
|
||||||
~*Out[2]:*~
|
## Was haben wir gemacht?
|
||||||
----
|
|
||||||
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
|
* `use std::io;` bindet die Standard-IO Bibliothek ein
|
||||||
* `let mut guess` legt eine Variable `guess` an
|
* `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
|
* `String::new()` erstellt eine neue Instanz der `String`-Klasse
|
||||||
* `io::stdin()` legt ein `Stdin`-Objekt an - ein Handler für die
|
* `io::stdin()` legt ein `Stdin`-Objekt an - ein Handler für die CLI-Eingabe
|
||||||
CLI-Eingabe
|
- ohne die ``use'' Anweisung oben, müsste es `std::io::stdin()` sein
|
||||||
** 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
|
* `.read_line(&mut guess)` ließt eine Zeile und speichert sie in guess
|
||||||
** `&` erstellt dabei eine Referenz (wie in C)
|
- `&` erstellt dabei eine Referenz (wie in C)
|
||||||
** Referenzen sind standardmäßig immutable - deshalb `&mut`
|
- Referenzen sind standardmäßig immutable - deshalb `&mut`
|
||||||
** `read_line()` gibt ein `Result`-Objekt zurück, dieser kann `Ok` oder
|
- `read_line()` gibt ein `Result`-Objekt zurück, dieser kann `Ok` oder `Err` enthalten
|
||||||
`Err` enthalten
|
|
||||||
* `.expect("Fehlermeldung")` entpackt das `Result`-Objekt
|
* `.expect("Fehlermeldung")` entpackt das `Result`-Objekt
|
||||||
** Theoretisch ist das unnötig, sonst gibt es aber eine Warnung
|
- Theoretisch ist das unnötig, sonst gibt es aber eine Warnung
|
||||||
** Sollte ein `Err` im Result sein, wird durch `expect()` eine Exception
|
- Sollte ein `Err` im Result sein, wird durch `expect()` eine Exception auftreten
|
||||||
auftreten
|
|
||||||
* `println!("Eingabe: {}", guess)` ist ein formatiertes print
|
* `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. +
|
Für eine random Zahl brauchen wir die erste Dependency. +
|
||||||
Also `Cargo.toml` bearbeiten:
|
Also `Cargo.toml` bearbeiten:
|
||||||
|
|
||||||
[source, toml]
|
```toml
|
||||||
----
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.3.14"
|
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.
|
Die crate `rand` kann jetzt im Code verwendet werden.
|
||||||
|
|
||||||
|
```rust
|
||||||
~*In[3]:*~
|
|
||||||
[source, rust]
|
|
||||||
----
|
|
||||||
:dep rand = "0.3.15"
|
|
||||||
// Das ^ ist von Jupyter
|
|
||||||
|
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
||||||
|
|
||||||
println!("{}", secret_number);
|
println!("{}", secret_number);
|
||||||
----
|
```
|
||||||
|
|
||||||
|
Und schwubbs wird eine zufälle Zahl ausgegeben.
|
||||||
|
|
||||||
~*Out[3]:*~
|
## Höher oder tiefer?
|
||||||
----
|
|
||||||
37
|
|
||||||
----
|
|
||||||
|
|
||||||
== Höher oder tiefer?
|
Vergleichen wir doch einfach mal…
|
||||||
|
Aber was ist das? Ein Fehler??
|
||||||
|
|
||||||
Vergleichen wir doch einfach mal… +
|
<div class="notCompiling">
|
||||||
Ein Fehler?
|
```rust
|
||||||
|
|
||||||
|
|
||||||
~*In[4]:*~
|
|
||||||
[source.notCompiling, rust]
|
|
||||||
----
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
match guess.cmp(&secret_number) {
|
match guess.cmp(&secret_number) {
|
||||||
@ -118,30 +88,25 @@ match guess.cmp(&secret_number) {
|
|||||||
Ordering::Greater => println!("Too big!"),
|
Ordering::Greater => println!("Too big!"),
|
||||||
Ordering::Equal => println!("You win!"),
|
Ordering::Equal => println!("You win!"),
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Der Compiler sagt uns dann Folgendes:
|
||||||
|
|
||||||
~*Out[4]:*~
|
```rust
|
||||||
----
|
|
||||||
|
|
||||||
match guess.cmp(&secret_number) {
|
match guess.cmp(&secret_number) {
|
||||||
|
|
||||||
^^^^^^^^^^^^^^ expected struct `String`, found `u32`
|
^^^^^^^^^^^^^^ expected struct `String`, found `u32`
|
||||||
|
|
||||||
mismatched types
|
mismatched types
|
||||||
|
```
|
||||||
----
|
|
||||||
|
|
||||||
Unser `guess` ist ja ein `String`! Den kann man nicht einfach mit einem
|
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:
|
Wir müssen unser guess also umwandeln:
|
||||||
|
|
||||||
|
```rust
|
||||||
~*In[5]:*~
|
|
||||||
[source, rust]
|
|
||||||
----
|
|
||||||
let guess: u32 = guess.trim().parse().expect("Please type a number!");
|
let guess: u32 = guess.trim().parse().expect("Please type a number!");
|
||||||
----
|
```
|
||||||
|
|
||||||
`.strip()` entfernt Whitespace von beiden Seiten und `parse()` macht
|
`.strip()` entfernt Whitespace von beiden Seiten und `parse()` macht
|
||||||
eine Zahl draus.
|
eine Zahl draus.
|
||||||
@ -152,10 +117,7 @@ Datentypen für den selben Wert anlegen muss.
|
|||||||
|
|
||||||
Jetzt sollte das Vergleichen auch klappen!
|
Jetzt sollte das Vergleichen auch klappen!
|
||||||
|
|
||||||
|
```rust
|
||||||
~*In[6]:*~
|
|
||||||
[source, rust]
|
|
||||||
----
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
match guess.cmp(&secret_number) {
|
match guess.cmp(&secret_number) {
|
||||||
@ -163,26 +125,22 @@ match guess.cmp(&secret_number) {
|
|||||||
Ordering::Greater => println!("Too big!"),
|
Ordering::Greater => println!("Too big!"),
|
||||||
Ordering::Equal => println!("You win!"),
|
Ordering::Equal => println!("You win!"),
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
|
> Too big!
|
||||||
|
|
||||||
~*Out[6]:*~
|
Wuuh!
|
||||||
----
|
|
||||||
Too big!
|
|
||||||
()
|
|
||||||
----
|
|
||||||
|
|
||||||
== Nicht nur ein Versuch
|
## Nicht nur ein Versuch
|
||||||
|
|
||||||
Damit wir mehrmals raten können, brauchen wir eine Schleife.
|
Damit wir mehrmals raten können, brauchen wir eine Schleife.
|
||||||
|
|
||||||
|
```rust
|
||||||
~*In[7]:*~
|
|
||||||
[source, rust]
|
|
||||||
----
|
|
||||||
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
||||||
loop {
|
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!");
|
let guess: u32 = guess.trim().parse().expect("Please type a number!");
|
||||||
|
|
||||||
match guess.cmp(&secret_number) {
|
match guess.cmp(&secret_number) {
|
||||||
@ -191,46 +149,40 @@ loop {
|
|||||||
Ordering::Equal => println!("You win!"),
|
Ordering::Equal => println!("You win!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
|
> Number? 100
|
||||||
~*Out[7]:*~
|
> Too big!
|
||||||
----
|
> Number? 50
|
||||||
Number? 100
|
> Too big!
|
||||||
Too big!
|
> Number? 25
|
||||||
Number? 50
|
> Too small!
|
||||||
Too big!
|
> Number? 30
|
||||||
Number? 25
|
> Too small!
|
||||||
Too small!
|
> Number? 40
|
||||||
Number? 30
|
> Too small!
|
||||||
Too small!
|
> Number? 42
|
||||||
Number? 40
|
> Too small!
|
||||||
Too small!
|
> Number? 45
|
||||||
Number? 42
|
> You win!
|
||||||
Too small!
|
> Number? 45
|
||||||
Number? 45
|
> You win!
|
||||||
You win!
|
> Number? 100
|
||||||
Number? 45
|
> Too big!
|
||||||
You win!
|
> Number? 45
|
||||||
Number? 100
|
> You win!
|
||||||
Too big!
|
> Number?
|
||||||
Number? 45
|
> ...
|
||||||
You win!
|
|
||||||
Number?
|
|
||||||
...
|
|
||||||
----
|
|
||||||
|
|
||||||
Funktioniert, aber selbst nach dem Erraten passiert nichts und wir
|
Funktioniert, aber selbst nach dem Erraten passiert nichts und wir
|
||||||
sollen weiter raten. +
|
sollen weiter raten. Offensichtlich müssen wir die Schleife noch abbrechen.
|
||||||
Offensichtlich müssen wir die Schleife dann abbrechen.
|
|
||||||
|
|
||||||
|
```rust
|
||||||
~*In[8]:*~
|
|
||||||
[source, rust]
|
|
||||||
----
|
|
||||||
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
let secret_number: u32 = rand::thread_rng().gen_range(1, 101);
|
||||||
loop {
|
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!");
|
let guess: u32 = guess.trim().parse().expect("Please type a number!");
|
||||||
|
|
||||||
match guess.cmp(&secret_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]:*~
|
## Error handling
|
||||||
----
|
|
||||||
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:
|
||||||
|
|
||||||
Derzeit stirbt das Programm einfach mit einem Fehler, wenn man keine
|
```rust
|
||||||
Zahl eingibt. Das können wir auch relativ einfach fixen:
|
|
||||||
|
|
||||||
|
|
||||||
~*In[9]:*~
|
|
||||||
[source, rust]
|
|
||||||
----
|
|
||||||
loop {
|
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() {
|
let guess: u32 = match guess.trim().parse() {
|
||||||
Ok(num) => num,
|
Ok(num) => num,
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
@ -288,17 +234,12 @@ loop {
|
|||||||
// Wenn wir hier her kommen, haben wir eine gültige Zahl und beenden einfach.
|
// Wenn wir hier her kommen, haben wir eine gültige Zahl und beenden einfach.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
|
> Number? a
|
||||||
~*Out[9]:*~
|
> Number? b
|
||||||
----
|
> Number? 🦀
|
||||||
Number? a
|
> Number? 5
|
||||||
Number? b
|
|
||||||
Number? 🦀
|
|
||||||
Number? 5
|
|
||||||
()
|
|
||||||
----
|
|
||||||
|
|
||||||
Statt einem `expect()` haben wir nun eine `match`-Expression. Die Syntax
|
Statt einem `expect()` haben wir nun eine `match`-Expression. Die Syntax
|
||||||
ist relativ einfach zu verstehen. Man kann auch mehrere `Ok(value)`
|
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
|
Das `num` nach dem Pfeil ist ein implizites Return. Wenn eine Variable
|
||||||
am Ende eines Blocks steht, wird sie zurückgegeben.
|
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
|
## Variablen
|
||||||
=== Mutability
|
### Mutability
|
||||||
|
|
||||||
Standardmäßig sind Variablen nicht mutable, also nicht veränderbar.
|
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`!
|
In anderen Sprachen ist das häufig `const` - in Rust gibt es aber auch `const`!
|
||||||
|
|
||||||
Das Folgende funktioniert also nicht:
|
Das Folgende funktioniert also nicht:
|
||||||
|
|
||||||
[source.notCompiling, Rust]
|
<div class="notCompiling">
|
||||||
----
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = "Hello world!";
|
let x = "Hello world!";
|
||||||
// Das folgende funktioniert nicht, weil x nicht mutable ist!
|
// Das folgende funktioniert nicht, weil x nicht mutable ist!
|
||||||
x = "Hello Rust!";
|
x = "Hello Rust!";
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
Damit Variablen mutable sind, muss `mut` genutzt werden:
|
Damit Variablen mutable sind, muss `mut` genutzt werden:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut x = "Hello world!";
|
let mut x = "Hello world!";
|
||||||
// Hier funktioniert es.
|
// Hier funktioniert es.
|
||||||
x = "Hello Rust!";
|
x = "Hello Rust!";
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
=== Constants
|
### Constants
|
||||||
|
|
||||||
Neben unveränderlichen Variablen gibt es auch noch Konstanten.
|
Neben unveränderlichen Variablen gibt es auch noch Konstanten.
|
||||||
Diese sind sehr ähnlich zu ersteren, haben aber zwei relevante Unterschiede:
|
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:
|
Ein Beispiel dafür ist folgendes:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
const MINUTES_IN_A_DAY: u32 = 24 * 60;
|
const MINUTES_IN_A_DAY: u32 = 24 * 60;
|
||||||
----
|
```
|
||||||
|
|
||||||
=== Shadowing
|
### Shadowing
|
||||||
|
|
||||||
Shadowing wurde beim Higher-Lower-Game schon einmal erwähnt.
|
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.
|
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.
|
Allerdings ist es mehr ein "Reuse" eines alten Namens.
|
||||||
Ein Beispiel:
|
Ein Beispiel:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = 5;
|
let x = 5;
|
||||||
let x = x + 5;
|
let x = x + 5;
|
||||||
|
|
||||||
println!("{}", x);
|
println!("{}", x);
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
Die Ausgabe des Programms ist dabei der letztere Wert, hier also 10.
|
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.
|
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.
|
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.
|
Jede Variable hat einen festen Datentyp.
|
||||||
Der Compiler kann häufig selber herausfinden, was für einer das ist, das ist die "Type Inference".
|
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.
|
Wenn das nicht geht, muss manuell ein Typ festgelegt werden.
|
||||||
|
|
||||||
Ein Beispiel:
|
Ein Beispiel:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
let guess: u32 = "42".parse().expect("Not a number!");
|
let guess: u32 = "42".parse().expect("Not a number!");
|
||||||
----
|
```
|
||||||
|
|
||||||
`"42"` ist offensichtlich ein String.
|
`"42"` ist offensichtlich ein String.
|
||||||
`parse()` kann verschiedene Ergebnisse-Datentypen erzeugen.
|
`parse()` kann verschiedene Ergebnisse-Datentypen erzeugen.
|
||||||
Das Ergebnis kann also verschiedene Typen haben, wir wollen ja aber wissen, was `guess` ist.
|
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.
|
Hier muss also `guess: u32` angegeben werden, sonst gibt es einen Fehler vom Compiler.
|
||||||
|
|
||||||
=== Scalar Types
|
### Scalar Types
|
||||||
Skalar heißt: ein einziges Value.
|
Skalar heißt: ein einziges Value.
|
||||||
Also eine Zahl (integer/float), Boolean oder ein einzelner Character.
|
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".
|
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.
|
"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
|
- `overflowing_add` gibt einen Boolean, ob ein Overflow auftritt
|
||||||
- `saturating_add` bleibt beim Maximum oder Minimum des verfügbaren Bereiches
|
- `saturating_add` bleibt beim Maximum oder Minimum des verfügbaren Bereiches
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
let number: u8 = 254;
|
let number: u8 = 254;
|
||||||
println!("{}", number.wrapping_add(2));
|
println!("{}", number.wrapping_add(2));
|
||||||
----
|
```
|
||||||
|
|
||||||
Die Ausgabe des Programms ist 0.
|
Die Ausgabe des Programms ist 0.
|
||||||
|
|
||||||
==== Floats
|
#### Floats
|
||||||
Sind normale IEEE-754 floats mit 32 oder 64 Bit.
|
Sind normale IEEE-754 floats mit 32 oder 64 Bit.
|
||||||
|
|
||||||
==== Boolean
|
#### Boolean
|
||||||
Auch nichts besonders, `true` oder `false` halt.
|
Auch nichts besonders, `true` oder `false` halt.
|
||||||
|
|
||||||
==== Chars
|
#### Chars
|
||||||
Sind besonders.
|
Sind besonders.
|
||||||
Einzelne Character in Rust sind nicht einfach wie in C ein u8 unter anderem Namen, sondern wirklich ein Zeichen.
|
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 `'🐧'`.
|
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.
|
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.
|
Gruppierung von mehreren Werten in einem Typ.
|
||||||
|
|
||||||
==== Tupel
|
#### Tupel
|
||||||
Tupel sind weird.
|
Tupel sind weird.
|
||||||
Sie haben eine feste Länge (wie C-Arrays), können aber verschiedene Datentypen beinhalten, also wie in Python.
|
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.
|
Sie sind aber schreibbar, wenn `mut` zur Initialisierung genutzt wird, also nicht wie in Python.
|
||||||
|
|
||||||
Ein paar Beispiele als Code:
|
Ein paar Beispiele als Code:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
let x: (f32, char, u8) = (1.0, '🐧', 3);
|
let x: (f32, char, u8) = (1.0, '🐧', 3);
|
||||||
//_x.0 = 2.0; // geht nicht, da x nicht mut ist.
|
//_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);
|
println!("{}", x.2);
|
||||||
|
|
||||||
//x.2 = 1.0; // Das geht nicht, da x.2 ein u8 ist.
|
//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.
|
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.
|
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.
|
Für "Arrays" mit veränderbarer Länge gibt es Vektoren.
|
||||||
|
|
||||||
Wieder etwas Code:
|
Wieder etwas Code:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
let x: [i32; 5] = [1, 2, 3, 4, 5];
|
let x: [i32; 5] = [1, 2, 3, 4, 5];
|
||||||
// ^ so sieht der Datentyp aus
|
// ^ so sieht der Datentyp aus
|
||||||
|
|
||||||
@ -187,19 +185,18 @@ println!("{}", x[0]); // 1, so wie immer
|
|||||||
|
|
||||||
let mut x = [15; 3]; // -> [15, 15, 15]
|
let mut x = [15; 3]; // -> [15, 15, 15]
|
||||||
x[0] = 16; // x = [16, 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.
|
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.
|
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`.
|
Sind wie normale Funktionen in C auch. Keyword ist `fn`.
|
||||||
|
|
||||||
Beispiel:
|
Beispiel:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
fn calculate_sum(a: i32, b: i32) -> i64 {
|
fn calculate_sum(a: i32, b: i32) -> i64 {
|
||||||
// Statements können natürlich normal genutzt werden
|
// Statements können natürlich normal genutzt werden
|
||||||
let c: i64 = a + b
|
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.
|
// Könnte aber auch einfach nur "a + b" sein.
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
== Kommentare
|
## Kommentare
|
||||||
|
|
||||||
Schon häufiger in den Beispielen - einfach `//`.
|
Schon häufiger in den Beispielen - einfach `//`.
|
||||||
Es gibt auch noch spezielle Docstrings, aber das kommt später.
|
Es gibt auch noch spezielle Docstrings, aber das kommt später.
|
||||||
|
|
||||||
== Kontrollfluss
|
## Kontrollfluss
|
||||||
=== `if`
|
### `if`
|
||||||
|
|
||||||
- ohne runde Klammern um die Bedingung
|
- ohne runde Klammern um die Bedingung
|
||||||
- _immer_ geschweifte Klammern, zumindest kein Beispiel ohne
|
- _immer_ geschweifte Klammern, zumindest kein Beispiel ohne
|
||||||
- Geht auch als short-if bei `let x = if condition { 5 } else { 6 }`
|
- Geht auch als short-if bei `let x = if condition { 5 } else { 6 }`
|
||||||
- Bedingung *muss* ein bool sein!
|
- Bedingung *muss* ein bool sein!
|
||||||
|
|
||||||
=== `loop`
|
### `loop`
|
||||||
|
|
||||||
- Basically ein `while (true)`
|
- Basically ein `while (true)`
|
||||||
- `break` und `continue`
|
- `break` und `continue`
|
||||||
@ -233,8 +230,7 @@ Es gibt auch noch spezielle Docstrings, aber das kommt später.
|
|||||||
|
|
||||||
Beispiel für labels:
|
Beispiel für labels:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
fn main() {
|
fn main() {
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
let mut a = 1;
|
let mut a = 1;
|
||||||
@ -246,15 +242,14 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
==== Ergebnis aus der Loop
|
#### Ergebnis aus der Loop
|
||||||
|
|
||||||
`break` mit Wert ist Rückgabe.
|
`break` mit Wert ist Rückgabe.
|
||||||
Einfaches Beispiel:
|
Einfaches Beispiel:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
|
|
||||||
@ -268,19 +263,18 @@ fn main() {
|
|||||||
|
|
||||||
println!("{}", result); // 20
|
println!("{}", result); // 20
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
=== `while`
|
### `while`
|
||||||
|
|
||||||
- nutzt auch keine runden Klammern
|
- nutzt auch keine runden Klammern
|
||||||
- sonst normal
|
- sonst normal
|
||||||
|
|
||||||
=== `for`
|
### `for`
|
||||||
|
|
||||||
Looped durch eine Collection (wie in Python).
|
Looped durch eine Collection (wie in Python).
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let a = [10, 20, 30, 40, 50];
|
let a = [10, 20, 30, 40, 50];
|
||||||
|
|
||||||
@ -288,4 +282,4 @@ fn main() {
|
|||||||
println!("{}", element);
|
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 hat eine Variable, die ihn "besitzt".
|
||||||
Jeder Wert kann zu einem Zeitpunkt nur von _einer_ Variable besessen werden.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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:
|
Ein Beispiel:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
let mut x = String::from("Hello"); // Legt "dynamischen" String an
|
let mut x = String::from("Hello"); // Legt "dynamischen" String an
|
||||||
x.push_str(" world!"); // Konkatiniert an den String
|
x.push_str(" world!"); // Konkatiniert an den String
|
||||||
|
|
||||||
println!("{}", x); // "Hello world!"
|
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.
|
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.
|
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 x = 5; // Int -> feste Größe und auf Stack
|
||||||
let y = x;
|
let y = x;
|
||||||
|
|
||||||
let s1 = String::from("Hello world"); // Dynamischer String auf Heap
|
let s1 = String::from("Hello world"); // Dynamischer String auf Heap
|
||||||
let s2 = s1;
|
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.
|
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".
|
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.
|
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!
|
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.
|
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?
|
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`.
|
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.
|
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.
|
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.
|
Damit werden (unter anderem) Race Conditions schon beim Compilen verhindert.
|
||||||
|
|
||||||
=== Dangling references
|
### Dangling references
|
||||||
|
|
||||||
[source.notCompiling, rust]
|
<div class="notCompiling">
|
||||||
----
|
```rust
|
||||||
fn dangle() -> &String {
|
fn dangle() -> &String {
|
||||||
let s = String::from("hello");
|
let s = String::from("hello");
|
||||||
&s // Referenz auf s returned
|
&s // Referenz auf s returned
|
||||||
} // Hier fliegt s aus dem Scope
|
} // Hier fliegt s aus dem Scope
|
||||||
----
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
Hier ist eine Funktion gebaut, die nur eine Referenz zurückgibt.
|
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.
|
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 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...
|
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.
|
Wenn wir auf Arrays arbeiten, wäre es ja cool, an verschiedenen Stellen gleichzeitig zu arbeiten.
|
||||||
Nur so kann multithreading etc. funktionieren.
|
Nur so kann multithreading etc. funktionieren.
|
||||||
@ -133,13 +136,12 @@ Nur so kann multithreading etc. funktionieren.
|
|||||||
Dafür hat Rust den Slice-Datentyp.
|
Dafür hat Rust den Slice-Datentyp.
|
||||||
Der funktioniert ähnlich wie Array-Ranges in Python.
|
Der funktioniert ähnlich wie Array-Ranges in Python.
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
let s = String::from("hello world");
|
let s = String::from("hello world");
|
||||||
|
|
||||||
let hello = &s[0..5];
|
let hello = &s[0..5];
|
||||||
let world = &s[6..11];
|
let world = &s[6..11];
|
||||||
----
|
```
|
||||||
|
|
||||||
Rust kümmert sich dabei darum, dass wir jetzt keinen Unsinn mehr mit `s` machen.
|
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.
|
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++.
|
Structs kennt man ja aus C/C++.
|
||||||
Man kann es (denke ich) auch mit JavaScript Objekten vergleichen.
|
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.
|
In Structs gruppiert man zusammengehöriges Zeug und hat so eine Art Pseudo-OOP.
|
||||||
Man kann damit neue Datentypen machen.
|
Man kann damit neue Datentypen machen.
|
||||||
|
|
||||||
== How to
|
## How to
|
||||||
|
|
||||||
=== "Normale" Structs
|
### "Normale" Structs
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
struct User {
|
struct User {
|
||||||
active: bool,
|
active: bool,
|
||||||
username: String,
|
username: String,
|
||||||
@ -38,59 +41,56 @@ fn main() {
|
|||||||
|
|
||||||
user1.email = String::from("anotheremail@example.com");
|
user1.email = String::from("anotheremail@example.com");
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
Hinweis: Es können nicht einzelne Felder mutable sein, sondern wenn dann immer das ganze Struct.
|
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 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:
|
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 {
|
let user2 = User {
|
||||||
email: String::from("another@example.com"),
|
email: String::from("another@example.com"),
|
||||||
..user1
|
..user1
|
||||||
};
|
};
|
||||||
----
|
```
|
||||||
|
|
||||||
`..user1` *muss* als letztes kommen und füllt dann alle bisher nicht gesetzten Felder.
|
`..user1` *muss* als letztes kommen und füllt dann alle bisher nicht gesetzten Felder.
|
||||||
Außerdem ist das etwas tricky:
|
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.
|
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.
|
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);
|
struct RGBColor(u8, u8, u8);
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let black = Color(0, 0, 0)
|
let black = Color(0, 0, 0)
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
Sind nutzbar wie Tupel (destrucuture und `.index` zum Zugriff auf Werte), allerdings eben ein eigener Typ.
|
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;
|
struct AlwaysEqual;
|
||||||
----
|
```
|
||||||
|
|
||||||
Ein Struct muss keine Felder haben.
|
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.
|
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.
|
Nur dann macht diese Art von Struct irgendwie Sinn.
|
||||||
|
|
||||||
== Ownership der Felder
|
## Ownership der Felder
|
||||||
|
|
||||||
Im ersten Beispiel wird `String` satt `&str` genutzt.
|
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.
|
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.
|
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.
|
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`.
|
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.
|
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.
|
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.
|
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:
|
Die folgenden Beispiele sollten relativ viel erklären:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
struct Rectangle {
|
struct Rectangle {
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
@ -177,15 +176,15 @@ fn main() {
|
|||||||
println!("{}", rect2.has_same_area(&rect1)); // true
|
println!("{}", rect2.has_same_area(&rect1)); // true
|
||||||
println!("{}", Rectangle::same_area(&rect1, &rect2)); // true
|
println!("{}", Rectangle::same_area(&rect1, &rect2)); // true
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
=== `&mut self`
|
### `&mut self`
|
||||||
|
|
||||||
Eine Methode kann auch `&mut self` als ersten Parameter haben.
|
Eine Methode kann auch `&mut self` als ersten Parameter haben.
|
||||||
Dann können auch Felder geschrieben werden. In diesem Fall werden Referenzen aber invalidiert!
|
Dann können auch Felder geschrieben werden. In diesem Fall werden Referenzen aber invalidiert!
|
||||||
|
|
||||||
[source, rust]
|
<div class="notCompiling">
|
||||||
----
|
```rust
|
||||||
struct Rectangle {
|
struct Rectangle {
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
@ -207,4 +206,5 @@ fn main() {
|
|||||||
|
|
||||||
println!("{}", ref1.width); // <- geht nicht!
|
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.
|
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.
|
"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`).
|
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.
|
Dieser ist entweder `None` oder `Some(value: T)` - es kann also ein Wert zusätzlich zur "Definition" beinhalten.
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
enum Farbcode {
|
enum Farbcode {
|
||||||
Hex,
|
Hex,
|
||||||
Rgb,
|
Rgb,
|
||||||
}
|
}
|
||||||
|
|
||||||
let hexcolor = Farbcode::Hex;
|
let hexcolor = Farbcode::Hex;
|
||||||
----
|
```
|
||||||
|
|
||||||
`Farbcode` ist also ein im Code benutzbarer Datentyp, genauso wie `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.
|
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:
|
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 {
|
enum Farbcode {
|
||||||
Hex(String),
|
Hex(String),
|
||||||
Rgb(u8, u8, u8),
|
Rgb(u8, u8, u8),
|
||||||
@ -48,14 +50,13 @@ enum Farbcode {
|
|||||||
|
|
||||||
let hexcode = Farbcode::Hex(String::from("00affe"));
|
let hexcode = Farbcode::Hex(String::from("00affe"));
|
||||||
let rgbcode = Farbcode::Rgb(125, 255, 255);
|
let rgbcode = Farbcode::Rgb(125, 255, 255);
|
||||||
----
|
```
|
||||||
|
|
||||||
Natürlich können die Structs jeder Art sein.
|
Natürlich können die Structs jeder Art sein.
|
||||||
Enums sind aber auch selber eine Art Struct.
|
Enums sind aber auch selber eine Art Struct.
|
||||||
Also können wir für Enums auch Methoden definieren wie für Structs.
|
Also können wir für Enums auch Methoden definieren wie für Structs.
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
impl Farbcode {
|
impl Farbcode {
|
||||||
fn to_css_string(&self) {
|
fn to_css_string(&self) {
|
||||||
// Methode, die für Hex und Rgb angewendet werden kann
|
// Methode, die für Hex und Rgb angewendet werden kann
|
||||||
@ -64,18 +65,18 @@ impl Farbcode {
|
|||||||
|
|
||||||
let rgbcode = Farbcode::Rgb(125, 255, 255);
|
let rgbcode = Farbcode::Rgb(125, 255, 255);
|
||||||
rgbcode.to_css_string();
|
rgbcode.to_css_string();
|
||||||
----
|
```
|
||||||
|
|
||||||
Tatsächlich ist damit so etwas wie Vererbung implementierbar.
|
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.
|
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.
|
Options hab ich oben schonmal kurz beschrieben.
|
||||||
In Rust ist dieser Datentyp sehr wichtig.
|
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.
|
`match` ist quasi das `switch` von Rust.
|
||||||
Nur kann es auch prüfen, ob eine Variable einem Enum-Typen angehört.
|
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.
|
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.
|
Diese Methode muss ja, je nach Typ, völlig unterschiedlich funktionieren.
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
enum Farbcode {
|
enum Farbcode {
|
||||||
Hex(String),
|
Hex(String),
|
||||||
Rgb(u8, u8, u8),
|
Rgb(u8, u8, u8),
|
||||||
@ -108,26 +108,25 @@ fn main() {
|
|||||||
println!("{}", hexcode.to_css_string());
|
println!("{}", hexcode.to_css_string());
|
||||||
println!("{}", rgbcode.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.
|
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.
|
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.
|
Entweder muss ein `match` eines Enums jede mögliche Variante abgrasen oder es gibt zwei Alternativen.
|
||||||
|
|
||||||
`other` ist quasi das `default` von Rust.
|
`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.
|
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.
|
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:
|
Letztendlich ist es ganz simpel:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Muenzwurf {
|
enum Muenzwurf {
|
||||||
Kopf,
|
Kopf,
|
||||||
@ -149,4 +148,4 @@ fn main() {
|
|||||||
let ergebnis = Muenzwurf::Seite;
|
let ergebnis = Muenzwurf::Seite;
|
||||||
print_wurf(ergebnis); // Das glaub ich nicht! 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.
|
Rust hat ein sehr hierarchisches Konzept, was die Strukturierung von Projekten angeht.
|
||||||
|
|
||||||
Fangen wir mal von oben an:
|
Fangen wir mal von oben an:
|
||||||
|
|
||||||
=== Packages
|
### Packages
|
||||||
|
|
||||||
Packages bestehen aus Crates.
|
Packages bestehen aus Crates.
|
||||||
Sie fassen diese also quasi zusammen und in `Cargo.toml` wird definiert, wie die Crates zu bauen sind.
|
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).
|
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.
|
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.
|
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).
|
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".
|
Creates sind die eigentlichen "Module".
|
||||||
Es gibt zwei Arten: binary und library.
|
Es gibt zwei Arten: binary und library.
|
||||||
|
|
||||||
==== Binary Crates
|
#### Binary Crates
|
||||||
|
|
||||||
Diese Crates können zu einer ausführbaren Datei kompiliert werden.
|
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.
|
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.
|
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.
|
Innerhalb einer Crate können Module existieren.
|
||||||
Und hier ist auch schon wieder von OOP abgeschaut.
|
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.
|
Zusätzlich kann auch direkt inline ein Modul erstellt werden.
|
||||||
Ein Beispiel:
|
Ein Beispiel:
|
||||||
|
|
||||||
[source.notCompiling, rust]
|
<div class="notCompiling">
|
||||||
----
|
```rust
|
||||||
mod testmodul {
|
mod testmodul {
|
||||||
mod nested_modul {
|
mod nested_modul {
|
||||||
fn funktion() {
|
fn funktion() {
|
||||||
@ -74,14 +78,14 @@ fn main() {
|
|||||||
// Hello world! Geht nicht...
|
// Hello world! Geht nicht...
|
||||||
crate::testmodul::nested_modul::funktion();
|
crate::testmodul::nested_modul::funktion();
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
Das funktioniert noch *nicht*.
|
Das funktioniert noch *nicht*.
|
||||||
Denn standardmäßig ist alles private, was nicht explizit public ist.
|
Denn standardmäßig ist alles private, was nicht explizit public ist.
|
||||||
Damit wir den obigen Aufruf machen können, muss der Code so aussehen:
|
Damit wir den obigen Aufruf machen können, muss der Code so aussehen:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
mod testmodul {
|
mod testmodul {
|
||||||
pub mod nested_modul {
|
pub mod nested_modul {
|
||||||
pub fn funktion() {
|
pub fn funktion() {
|
||||||
@ -101,24 +105,23 @@ fn main() {
|
|||||||
// Hello world!
|
// Hello world!
|
||||||
crate::testmodul::nested_modul::funktion();
|
crate::testmodul::nested_modul::funktion();
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
Nur so kann auf Submodule und Funktionen dieser Module zugegriffen werden.
|
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.
|
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.
|
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.
|
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.
|
Bei Structs ist die Besonderheit, dass die einzelnen Attribute auch wieder private oder public sein können.
|
||||||
So kann man folgendes machen:
|
So kann man folgendes machen:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
mod testmodul {
|
mod testmodul {
|
||||||
pub struct Teststruct {
|
pub struct Teststruct {
|
||||||
pub oeffentlich: String,
|
pub oeffentlich: String,
|
||||||
@ -142,11 +145,11 @@ fn main() {
|
|||||||
// Geht nicht!
|
// Geht nicht!
|
||||||
// println!("Privat: {}", a.privat);
|
// println!("Privat: {}", a.privat);
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
Dagegen gilt für Enums: Wenn der Enum public ist, sind auch alle Varianten public.
|
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.
|
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.
|
Vektoren kennt man ja aus C++ als dynamische Alternative zu Arrays.
|
||||||
Es ist quasi eine Linked List, die beliebig erweiterbar bzw. manipulierbar ist.
|
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?
|
Wie benutze ich jetzt so einen Vector?
|
||||||
Hier einfach mal eine Übersicht:
|
Hier einfach mal eine Übersicht:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
// -- Erstellen --
|
// -- Erstellen --
|
||||||
// Mit dem vec!-Pragma
|
// Mit dem vec!-Pragma
|
||||||
let v = vec![1, 2, 3];
|
let v = vec![1, 2, 3];
|
||||||
@ -56,35 +59,35 @@ match v.get(2) {
|
|||||||
for i in &mut v {
|
for i in &mut v {
|
||||||
*i += 50;
|
*i += 50;
|
||||||
}
|
}
|
||||||
----
|
```
|
||||||
|
|
||||||
=== Achtung, Scope
|
### Achtung, Scope
|
||||||
|
|
||||||
Wenn ein Vector aus dem Scope fällt, wird er zusammen mit seinem Inhalt gedropped.
|
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.
|
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.
|
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.
|
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.
|
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?
|
Ein Vector kann nur einen Datentypen aufnehmen?
|
||||||
Der Datentyp kann aber auch ein Enum sein!
|
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.
|
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.
|
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?
|
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.
|
Es gibt im Core eigentlich nur `str`, also ein Slice.
|
||||||
Der `String`-Typ kommt aus der Standard-Lib und ist einfacher zu nutzen.
|
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.
|
String sind UTF-8 encoded, also egal mit was man sie bewirft, es sollte klappen.
|
||||||
Allerdings ist das Handling deshalb etwas kompliziert.
|
Allerdings ist das Handling deshalb etwas kompliziert.
|
||||||
Rust fasst das ganz gut am Ende der Seite zusammen mit
|
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:
|
Hier wieder eine Übersicht zur Nutzung:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
// -- Erstellen --
|
// -- Erstellen --
|
||||||
// String::from()
|
// String::from()
|
||||||
// "Hello ".to_string() macht das selbe
|
// "Hello ".to_string() macht das selbe
|
||||||
@ -131,27 +133,26 @@ let s2 = String::from("Stein");
|
|||||||
let s3 = String::from("Papier");
|
let s3 = String::from("Papier");
|
||||||
let s4 = format!("{}, {}, {}", s1, s2, s3);
|
let s4 = format!("{}, {}, {}", s1, s2, s3);
|
||||||
// Hier wird kein Ownership übergeben!
|
// Hier wird kein Ownership übergeben!
|
||||||
----
|
```
|
||||||
|
|
||||||
=== Indexing
|
### Indexing
|
||||||
|
|
||||||
Aus Python z.B. kennt man ja `"Hallo"[0] -> H`.
|
Aus Python z.B. kennt man ja `"Hallo"[0] -> H`.
|
||||||
In Rust geht das nicht.
|
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 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.
|
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.
|
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.
|
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.
|
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.
|
Über einem String iterieren geht ganz ok.
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
for c in "hallo".chars() {
|
for c in "hallo".chars() {
|
||||||
println!("{}", c);
|
println!("{}", c);
|
||||||
}
|
}
|
||||||
@ -162,19 +163,18 @@ for b in "hallo".bytes() {
|
|||||||
println!("{}", b);
|
println!("{}", b);
|
||||||
}
|
}
|
||||||
// Wirft eben die einzelnen u8 raus.
|
// 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.
|
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.
|
Der Erlöser der Programmierer und Lösung jeder Aufgabe bei der Bewerbung, die "O(n)" enthält.
|
||||||
Oder so ähnlich.
|
Oder so ähnlich.
|
||||||
|
|
||||||
Nutzung:
|
Nutzung:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
// Das hier ist für die "Abkürzungen"
|
// Das hier ist für die "Abkürzungen"
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -210,13 +210,13 @@ for (key, value) in &zahlwort {
|
|||||||
println!("{}: {}", key, value);
|
println!("{}: {}", key, value);
|
||||||
}
|
}
|
||||||
// Sehr selbsterklärend
|
// Sehr selbsterklärend
|
||||||
----
|
```
|
||||||
|
|
||||||
=== Ownership
|
### Ownership
|
||||||
|
|
||||||
Falls Key oder Value kein Copy Trait haben, wird der Ownership übergeben. Strings sind also danach ungültig.
|
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:
|
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.
|
Vielleicht werde ich sie irgendwann mal lösen, dann landet der Code hier.
|
||||||
|
|
||||||
=== Aufgabe 1
|
### Aufgabe 1
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut list = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
|
let mut list = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
list.sort();
|
list.sort();
|
||||||
let mid = list.len() / 2; // integer divide
|
let mid = list.len() / 2; // integer divide
|
||||||
println!("{}", list[mid]);
|
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.
|
Dieses Makro it furchtbar simpel: Es macht Panik und das Programm stirbt mit einem Fehler.
|
||||||
Diesen Fehler kann man auch nicht catchen.
|
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:
|
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]
|
[profile.release]
|
||||||
panic = 'abort'
|
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.
|
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.
|
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`:
|
Ein volles Beispiel mit ganz viel `match`:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::ErrorKind;
|
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!`.
|
Machen aus einem `Result<T, E>` entweder ein `T` oder eine `panic!`.
|
||||||
Bei `expect()` kann man noch die Fehlermeldung festlegen.
|
Bei `expect()` kann man noch die Fehlermeldung festlegen.
|
||||||
|
|
||||||
Warum man jemals `unwrap()` nehmen sollte, erschließt sich mir nicht ganz.
|
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.
|
Oft schreibt man Funktionen so, dass Fehler weiter "hochgegeben" werden, falls man welche bekommt.
|
||||||
`?` macht genau das bei einem Result.
|
`?` macht genau das bei einem Result.
|
||||||
Codemäßig erklärt:
|
Codemäßig erklärt:
|
||||||
|
|
||||||
[source, rust]
|
```rust
|
||||||
----
|
|
||||||
let a = match result {
|
let a = match result {
|
||||||
Ok(nummer) => nummer,
|
Ok(nummer) => nummer,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
@ -82,11 +83,11 @@ let a = match result {
|
|||||||
// Ergibt das selbe wie
|
// Ergibt das selbe wie
|
||||||
|
|
||||||
let a = result?;
|
let a = result?;
|
||||||
----
|
```
|
||||||
|
|
||||||
Das `?` kann auch für zum Beispiel `Option` verwendet werden, dann returned es natürlich `None`.
|
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.
|
Bis jetzt hat `main()` immer nichts, also implizit `()` zurückgegeben.
|
||||||
Manchmal wollen wir ja aber auch was anderes als "0" als return code haben.
|
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.
|
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:
|
Der Artikel ist sehr sehr sehr lang, aber eigentlich sagt er:
|
||||||
"Panic nur wenn es eben nicht gerettet werden kann."
|
"Panic nur wenn es eben nicht gerettet werden kann."
|
@ -109,6 +109,17 @@ const { title } = Astro.props;
|
|||||||
height: auto;
|
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 */
|
/* A custom indicator that this code is in fact faulty */
|
||||||
.notCompiling code {
|
.notCompiling code {
|
||||||
display: block;
|
display: block;
|
||||||
|
Loading…
Reference in New Issue
Block a user