Module
Mit mod gliederst du Code. Alles ist standardmäßig privat; pub macht es sichtbar.
mod geometrie {
pub const PI: f64 = 3.14159;
pub struct Kreis {
pub radius: f64,
id: u32, // privat
}
impl Kreis {
pub fn neu(radius: f64) -> Self { Kreis { radius, id: 1 } }
pub fn flaeche(&self) -> f64 { PI * self.radius * self.radius }
pub fn id(&self) -> u32 { self.id }
}
pub mod hilfe {
pub fn runde(x: f64) -> f64 { (x * 100.0).round() / 100.0 }
}
}
use geometrie::{hilfe::runde, Kreis};
fn main() {
let k = Kreis::neu(2.0);
println!("{} {}", runde(k.flaeche()), k.id());
println!("{}", geometrie::PI);
}12.57 1 3.14159
Pfade: crate:: (Wurzel), self::, super:: (Elternmodul). In echten Projekten steht jedes Modul in einer eigenen Datei (mod geometrie; lädt geometrie.rs).
Cargo
Cargo ist Build-Werkzeug und Paketmanager:
cargo new mein_projekt # neues Projekt
cargo run # bauen und starten
cargo build --release # optimiert bauen
cargo check # nur prüfen (schnell)
cargo test # Tests ausführen
cargo fmt # formatieren
cargo clippy # Lint-Hinweise
cargo add serde # Abhängigkeit hinzufügen
cargo doc --open # DokumentationDie Datei Cargo.toml:
[package]
name = "mein_projekt"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
rand = "0.8"Pakete heißen Crates und kommen von crates.io. Cargo.lock hält exakte Versionen fest.
Tests
Tests stehen im selben File in einem #[cfg(test)]-Modul (Unit-Tests) oder in tests/ (Integrationstests):
fn addiere(a: i32, b: i32) -> i32 { a + b }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn addition() {
assert_eq!(addiere(2, 3), 5);
}
#[test]
#[should_panic(expected = "Division")]
fn panik() {
panic!("Division durch null");
}
#[test]
fn mit_result() -> Result<(), String> {
if addiere(1, 1) == 2 { Ok(()) } else { Err("falsch".into()) }
}
}cargo test führt alle aus. Nützliche Makros: assert!, assert_eq!, assert_ne!. Dokumentationskommentare (///) können Beispielcode enthalten, der ebenfalls als Test läuft.
Fehlerbehandlung im Projekt
Für Anwendungen nutzt man oft die Crates anyhow (einfach) und thiserror (eigene Fehlertypen):
use anyhow::{Context, Result};
fn lade(pfad: &str) -> Result<String> {
std::fs::read_to_string(pfad).with_context(|| format!("konnte {pfad} nicht lesen"))
}Nützliche Crates
| Crate | Zweck |
|---|---|
serde, serde_json | (De-)Serialisierung |
tokio | asynchrone Programmierung |
reqwest | HTTP-Client |
clap | Kommandozeilenargumente |
axum, actix-web | Webserver |
rand | Zufall |
regex | Reguläre Ausdrücke |
chrono | Datum und Zeit |
Merke
modgliedert Code,pubmacht öffentlich,useimportiertcargo new/run/build/test/fmt/clippysind die wichtigsten BefehleCargo.tomllistet Abhängigkeiten (Crates von crates.io)- Tests:
#[test]undcargo test, Makrosassert_eq!usw. anyhow/thiserrorfür Fehler,serde/tokiofür den Alltag
Aufgabe
Lege mit cargo new ein Projekt an, schreibe eine Funktion mit zwei Tests und führe cargo test aus.