Rust.md
This commit is contained in:
@@ -0,0 +1,746 @@
|
|||||||
|
```md
|
||||||
|
---
|
||||||
|
title: "Rust Tutorial (für Python-Erfahrene)"
|
||||||
|
tags: [rust, tutorial, programming]
|
||||||
|
created: 2026-03-27
|
||||||
|
---
|
||||||
|
|
||||||
|
# Rust Tutorial (für Python-Erfahrene)
|
||||||
|
|
||||||
|
Dieses Tutorial ist als **hands-on Lernpfad** gedacht: viele kleine Programme, die du direkt ausführen und variieren kannst. Du bekommst dabei die wichtigsten Rust-Konzepte (Ownership/Borrowing, Lifetimes-Grundlagen, Traits, Enums/Pattern Matching, Fehlerbehandlung, Collections, Generics, Module/Crates, Testing, Async-Basics).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0) Setup: Was brauche ich, wie führe ich Rust aus?
|
||||||
|
|
||||||
|
### Installation (Compiler + Tooling)
|
||||||
|
Rust wird normalerweise über **rustup** installiert (inkl. `rustc`, `cargo`, Standardbibliothek).
|
||||||
|
|
||||||
|
- **Windows/macOS/Linux:**
|
||||||
|
- https://rustup.rs öffnen und Anweisungen folgen
|
||||||
|
- Danach prüfen:
|
||||||
|
```bash
|
||||||
|
rustc --version
|
||||||
|
cargo --version
|
||||||
|
rustup --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Editor (empfohlen)
|
||||||
|
- **VS Code** + Extension: **rust-analyzer**
|
||||||
|
- Alternativ: IntelliJ Rust, Neovim + rust-analyzer, etc.
|
||||||
|
|
||||||
|
### Neues Projekt erstellen und ausführen
|
||||||
|
Rust nutzt **Cargo** als Build-Tool + Paketmanager.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo new hello_rust
|
||||||
|
cd hello_rust
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
Wichtige Cargo-Kommandos:
|
||||||
|
```bash
|
||||||
|
cargo build # kompiliert (debug)
|
||||||
|
cargo build --release
|
||||||
|
cargo run # build + run
|
||||||
|
cargo test # tests
|
||||||
|
cargo fmt # formatieren (rustfmt)
|
||||||
|
cargo clippy # lints (clippy)
|
||||||
|
cargo doc --open # docs generieren + öffnen
|
||||||
|
```
|
||||||
|
|
||||||
|
Projektstruktur:
|
||||||
|
- `src/main.rs` (Binary)
|
||||||
|
- `Cargo.toml` (Dependencies, Name, Version, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1) Hello World + Grundsyntax
|
||||||
|
|
||||||
|
`src/main.rs`:
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, Rust!");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** Makro-Aufruf erkennst du am `!` (`println!`, `vec!`, `format!`, …).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2) Variablen, Mutabilität, Typen
|
||||||
|
|
||||||
|
Rust ist **statisch typisiert**, aber oft mit **Typinferenz**.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let x = 5; // immutable
|
||||||
|
// x = 6; // Fehler
|
||||||
|
|
||||||
|
let mut y = 5; // mutable
|
||||||
|
y = 6;
|
||||||
|
|
||||||
|
let z: i32 = 42; // expliziter Typ
|
||||||
|
let pi: f64 = 3.1415;
|
||||||
|
|
||||||
|
println!("{x} {y} {z} {pi}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shadowing (anders als mut)
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let s = "42";
|
||||||
|
let s = s.parse::<i32>().unwrap(); // shadowing: neuer s mit anderem Typ
|
||||||
|
println!("{s}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3) Strings: `&str` vs `String` (Python-Vergleich)
|
||||||
|
|
||||||
|
- `&str`: **String Slice**, meist “geliehener” Text (z.B. Stringliteral)
|
||||||
|
- `String`: **besitzender**, heap-allocierter String (änderbar)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let a: &str = "hi"; // slice
|
||||||
|
let mut b: String = String::from("hi"); // owned
|
||||||
|
b.push_str(" there");
|
||||||
|
|
||||||
|
println!("{a} / {b}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Sehr häufig konvertieren:
|
||||||
|
```rust
|
||||||
|
let s = "abc".to_string();
|
||||||
|
let t = String::from("abc");
|
||||||
|
let u: &str = &t; // String -> &str
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4) Funktionen
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn add(a: i32, b: i32) -> i32 {
|
||||||
|
a + b // kein Semikolon => Ausdruck
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let r = add(2, 3);
|
||||||
|
println!("{r}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Mehr Rückgabewerte via Tupel:
|
||||||
|
```rust
|
||||||
|
fn div_mod(a: i32, b: i32) -> (i32, i32) {
|
||||||
|
(a / b, a % b)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5) Kontrollfluss: `if`, `loop`, `while`, `for`
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let n = 7;
|
||||||
|
|
||||||
|
if n % 2 == 0 {
|
||||||
|
println!("even");
|
||||||
|
} else {
|
||||||
|
println!("odd");
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..3 {
|
||||||
|
println!("i={i}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = vec!["a", "b", "c"];
|
||||||
|
for (idx, item) in items.iter().enumerate() {
|
||||||
|
println!("{idx}: {item}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`loop` kann Werte zurückgeben:
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let mut i = 0;
|
||||||
|
let result = loop {
|
||||||
|
i += 1;
|
||||||
|
if i == 3 {
|
||||||
|
break i * 10; // liefert 30
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("{result}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6) Structs, Methoden, `impl`
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct User {
|
||||||
|
name: String,
|
||||||
|
age: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
fn new(name: impl Into<String>, age: u32) -> Self {
|
||||||
|
Self { name: name.into(), age }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn birthday(&mut self) {
|
||||||
|
self.age += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut u = User::new("Alice", 30);
|
||||||
|
u.birthday();
|
||||||
|
println!("{u:?}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7) Enums + Pattern Matching (sehr wichtig)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Message {
|
||||||
|
Quit,
|
||||||
|
Write(String),
|
||||||
|
Move { x: i32, y: i32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(m: Message) {
|
||||||
|
match m {
|
||||||
|
Message::Quit => println!("bye"),
|
||||||
|
Message::Write(text) => println!("text={text}"),
|
||||||
|
Message::Move { x, y } => println!("move to {x},{y}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
handle(Message::Write("hello".into()));
|
||||||
|
handle(Message::Move { x: 1, y: 2 });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`match` ist exhaustiv: alle Fälle müssen behandelt werden (oder `_`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8) Ownership, Borrowing, References (Kern von Rust)
|
||||||
|
|
||||||
|
Rust verhindert Data Races und Use-After-Free durch Regeln:
|
||||||
|
|
||||||
|
### Ownership-Regeln (vereinfacht)
|
||||||
|
1. Jeder Wert hat **genau einen Owner**.
|
||||||
|
2. Wenn der Owner aus dem Scope läuft, wird der Wert **gedroppt**.
|
||||||
|
3. **Move**: Zuweisung/Übergabe kann Ownership übertragen.
|
||||||
|
|
||||||
|
Beispiel: Move vs Copy
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let a = String::from("hello");
|
||||||
|
let b = a;
|
||||||
|
// println!("{a}"); // Fehler: a wurde gemoved
|
||||||
|
|
||||||
|
let x = 5; // i32 ist Copy
|
||||||
|
let y = x;
|
||||||
|
println!("{x} {y}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Borrowing: `&T` (immutable) und `&mut T` (mutable)
|
||||||
|
```rust
|
||||||
|
fn len(s: &String) -> usize {
|
||||||
|
s.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_exclamation(s: &mut String) {
|
||||||
|
s.push('!');
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut s = String::from("hi");
|
||||||
|
println!("{}", len(&s));
|
||||||
|
|
||||||
|
add_exclamation(&mut s);
|
||||||
|
println!("{s}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Borrowing-Regeln:
|
||||||
|
- beliebig viele `&T` **oder**
|
||||||
|
- genau ein `&mut T`
|
||||||
|
- aber nicht beides gleichzeitig (im gleichen Gültigkeitsbereich)
|
||||||
|
|
||||||
|
Typischer Fehler (und Fix durch Scopes):
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let mut s = String::from("abc");
|
||||||
|
|
||||||
|
let r1 = &s;
|
||||||
|
let r2 = &s;
|
||||||
|
println!("{r1} {r2}");
|
||||||
|
|
||||||
|
let r3 = &mut s; // ok, weil r1/r2 danach nicht mehr benutzt werden
|
||||||
|
r3.push('d');
|
||||||
|
println!("{r3}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9) Slices: Teilansichten von Daten
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let a = [10, 20, 30, 40];
|
||||||
|
let mid: &[i32] = &a[1..3]; // 20,30
|
||||||
|
println!("{mid:?}");
|
||||||
|
|
||||||
|
let s = String::from("hello world");
|
||||||
|
let w: &str = &s[0..5];
|
||||||
|
println!("{w}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Achtung bei UTF-8: String-Slicing nur an gültigen Byte-Grenzen von Codepoints.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10) Collections: `Vec`, `HashMap`, `HashSet`
|
||||||
|
|
||||||
|
### `Vec<T>`
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let mut v = vec![1, 2, 3];
|
||||||
|
v.push(4);
|
||||||
|
|
||||||
|
for x in &v {
|
||||||
|
println!("{x}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(last) = v.pop() {
|
||||||
|
println!("popped {last}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `HashMap<K,V>`
|
||||||
|
```rust
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
m.insert("alice", 10);
|
||||||
|
m.insert("bob", 7);
|
||||||
|
|
||||||
|
*m.entry("alice").or_insert(0) += 1;
|
||||||
|
|
||||||
|
if let Some(score) = m.get("alice") {
|
||||||
|
println!("alice={score}");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k, v) in &m {
|
||||||
|
println!("{k} => {v}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11) Option und Result: Fehlerbehandlung ohne Exceptions
|
||||||
|
|
||||||
|
Rust nutzt **`Option<T>`** (statt `None`) und **`Result<T, E>`** (statt Exceptions).
|
||||||
|
|
||||||
|
### Option
|
||||||
|
```rust
|
||||||
|
fn first(v: &[i32]) -> Option<i32> {
|
||||||
|
v.first().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v = vec![1, 2, 3];
|
||||||
|
match first(&v) {
|
||||||
|
Some(x) => println!("first={x}"),
|
||||||
|
None => println!("empty"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Result + `?` Operator
|
||||||
|
```rust
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
fn read_file(path: &str) -> Result<String, std::io::Error> {
|
||||||
|
let text = fs::read_to_string(path)?;
|
||||||
|
Ok(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
match read_file("Cargo.toml") {
|
||||||
|
Ok(t) => println!("len={}", t.len()),
|
||||||
|
Err(e) => eprintln!("error: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**`?`**: Wenn `Err`, returnt die Funktion früh; wenn `Ok`, entpackt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12) Iterators (sehr “pythonic”, aber typisiert)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let nums = vec![1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
let squares: Vec<i32> = nums.iter()
|
||||||
|
.map(|x| x * x)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let even_sum: i32 = nums.iter()
|
||||||
|
.filter(|x| *x % 2 == 0)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
println!("{squares:?} even_sum={even_sum}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ownership dabei:
|
||||||
|
- `iter()` gibt `&T`
|
||||||
|
- `into_iter()` konsumiert und gibt `T` (Owned)
|
||||||
|
- `iter_mut()` gibt `&mut T`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13) Generics + Traits (Rusts “Interfaces”)
|
||||||
|
|
||||||
|
### Generics
|
||||||
|
```rust
|
||||||
|
fn first<T: Clone>(v: &[T]) -> Option<T> {
|
||||||
|
v.first().cloned()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Trait definieren und implementieren
|
||||||
|
```rust
|
||||||
|
trait Greeter {
|
||||||
|
fn greet(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Person { name: String }
|
||||||
|
|
||||||
|
impl Greeter for Person {
|
||||||
|
fn greet(&self) -> String {
|
||||||
|
format!("Hi, {}!", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn say_hello(g: &impl Greeter) {
|
||||||
|
println!("{}", g.greet());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let p = Person { name: "Alice".into() };
|
||||||
|
say_hello(&p);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Trait Bounds:
|
||||||
|
```rust
|
||||||
|
fn print_debug<T: std::fmt::Debug>(x: T) {
|
||||||
|
println!("{x:?}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14) Module, Crates, Sichtbarkeit (`pub`)
|
||||||
|
|
||||||
|
### In einer Datei
|
||||||
|
```rust
|
||||||
|
mod math {
|
||||||
|
pub fn add(a: i32, b: i32) -> i32 {
|
||||||
|
a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn secret() {} // privat
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("{}", math::add(1, 2));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mehrere Dateien (typisches Layout)
|
||||||
|
- `src/main.rs`
|
||||||
|
- `src/math.rs`
|
||||||
|
|
||||||
|
`src/main.rs`:
|
||||||
|
```rust
|
||||||
|
mod math;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("{}", math::add(1, 2));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`src/math.rs`:
|
||||||
|
```rust
|
||||||
|
pub fn add(a: i32, b: i32) -> i32 {
|
||||||
|
a + b
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15) Lifetimes (Grundidee, praxisnah)
|
||||||
|
|
||||||
|
Lifetimes sagen dem Compiler: “Wie lange sind Referenzen gültig?”
|
||||||
|
|
||||||
|
Beispiel: Funktion gibt eine Referenz zurück:
|
||||||
|
```rust
|
||||||
|
fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
|
||||||
|
if a.len() >= b.len() { a } else { b }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = "short";
|
||||||
|
let y = "a bit longer";
|
||||||
|
println!("{}", longer(x, y));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Als Anfänger-Regel:
|
||||||
|
- Wenn du **Owned** (`String`, `Vec<T>`) zurückgeben kannst, ist das oft einfacher.
|
||||||
|
- Lifetimes brauchst du vor allem bei APIs, die Referenzen zurückgeben.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16) Smart Pointers: `Box`, `Rc`, `Arc`, `RefCell` (Überblick)
|
||||||
|
|
||||||
|
- `Box<T>`: heap allocation, Ownership bleibt eindeutig
|
||||||
|
- `Rc<T>`: shared ownership **single-thread**
|
||||||
|
- `Arc<T>`: shared ownership **thread-safe**
|
||||||
|
- `RefCell<T>`: “interior mutability” (Borrow-Regeln zur Laufzeit)
|
||||||
|
|
||||||
|
Mini-Beispiel `Rc`:
|
||||||
|
```rust
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = Rc::new(String::from("hello"));
|
||||||
|
let b = Rc::clone(&a);
|
||||||
|
|
||||||
|
println!("a={}, b={}", a, b);
|
||||||
|
println!("strong_count={}", Rc::strong_count(&a));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17) Concurrency (Basics): Threads + Channels
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::thread;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let handle = thread::spawn(move || {
|
||||||
|
tx.send("hello from thread").unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
let msg = rx.recv().unwrap();
|
||||||
|
println!("got: {msg}");
|
||||||
|
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18) Async (Basics): `async/await` mit Tokio
|
||||||
|
|
||||||
|
Async braucht i.d.R. eine Runtime (z.B. Tokio).
|
||||||
|
|
||||||
|
`Cargo.toml`:
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
`src/main.rs`:
|
||||||
|
```rust
|
||||||
|
use tokio::time::{sleep, Duration};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let h = tokio::spawn(async {
|
||||||
|
sleep(Duration::from_millis(100)).await;
|
||||||
|
42
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = h.await.unwrap();
|
||||||
|
println!("result={result}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 19) Testing
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn add(a: i32, b: i32) -> i32 { a + b }
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adds() {
|
||||||
|
assert_eq!(add(2, 3), 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ausführen:
|
||||||
|
```bash
|
||||||
|
cargo test
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 20) “Rust für Python-Dev”: mentale Übersetzungen
|
||||||
|
|
||||||
|
- Python-Liste ↔ `Vec<T>`
|
||||||
|
- Dict ↔ `HashMap<K,V>`
|
||||||
|
- `None` ↔ `Option<T>`
|
||||||
|
- Exceptions ↔ `Result<T,E>` + `?`
|
||||||
|
- Duck typing ↔ Traits + Generics
|
||||||
|
- Mutable Default überall ↔ Rust: **immutable by default**
|
||||||
|
- Garbage Collector ↔ Ownership (Drop am Scope-Ende)
|
||||||
|
|
||||||
|
**Idiom:** Daten lieber so modellieren, dass `match` alle Fälle abdeckt (Enums sind stark).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 21) Mini-Projekt 1: CLI “todo” (ohne externe Crates)
|
||||||
|
|
||||||
|
Ziel: einfache Aufgabenliste im Speicher (kein Persistenz).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut todos: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
print!("todo> ");
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
|
||||||
|
let mut line = String::new();
|
||||||
|
if io::stdin().read_line(&mut line).is_err() {
|
||||||
|
println!("input error");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let line = line.trim();
|
||||||
|
|
||||||
|
if line == "quit" {
|
||||||
|
break;
|
||||||
|
} else if line == "list" {
|
||||||
|
for (i, t) in todos.iter().enumerate() {
|
||||||
|
println!("{}: {}", i + 1, t);
|
||||||
|
}
|
||||||
|
} else if let Some(rest) = line.strip_prefix("add ") {
|
||||||
|
todos.push(rest.to_string());
|
||||||
|
} else if let Some(rest) = line.strip_prefix("done ") {
|
||||||
|
if let Ok(idx) = rest.parse::<usize>() {
|
||||||
|
if idx >= 1 && idx <= todos.len() {
|
||||||
|
todos.remove(idx - 1);
|
||||||
|
} else {
|
||||||
|
println!("index out of range");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("usage: done <number>");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("commands: add <text> | list | done <n> | quit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 22) Mini-Projekt 2: JSON holen (Reqwest)
|
||||||
|
|
||||||
|
`Cargo.toml`:
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
`src/main.rs`:
|
||||||
|
```rust
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct HttpBin {
|
||||||
|
url: String,
|
||||||
|
origin: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), reqwest::Error> {
|
||||||
|
let data: HttpBin = reqwest::get("https://httpbin.org/get")
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
println!("{data:#?}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 23) Nächste Schritte / gute Referenzen
|
||||||
|
|
||||||
|
- Offizielles Buch: https://doc.rust-lang.org/book/
|
||||||
|
- Rust by Example: https://doc.rust-lang.org/rust-by-example/
|
||||||
|
- Standard Library Docs: https://doc.rust-lang.org/std/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vorschlag für deinen Lernpfad (kurz)
|
||||||
|
1. Kapitel 1–7 (Syntax, Structs/Enums)
|
||||||
|
2. Ownership/Borrowing (Kapitel 8–10) – viel üben
|
||||||
|
3. Option/Result + Iterators
|
||||||
|
4. Traits/Generics + Module
|
||||||
|
5. Tests + kleines Projekt
|
||||||
|
6. Dann Async/Concurrency nach Bedarf
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Offene Fragen (damit ich es besser anpassen kann)
|
||||||
|
- Willst du Rust eher für **CLI**, **Backend**, **Embedded**, **WebAssembly** oder **Data/Perf** nutzen?
|
||||||
|
- Betriebssystem/Editor?
|
||||||
|
- Soll ich daraus **mehrere Obsidian-Notizen** machen (z.B. “Ownership”, “Result/Option”, “Traits”, …) mit internen Links?
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user