This commit is contained in:
2026-04-02 22:31:09 +02:00
parent 5811cbd9e9
commit 6648179294
+746
View File
@@ -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 17 (Syntax, Structs/Enums)
2. Ownership/Borrowing (Kapitel 810) 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?
```