EMZETT.
Login

Kurz: Mit mod gliederst du Code. Alles ist standardmäßig privat; pub macht es sichtbar.

Teil des Kurses Rust

Kapitel 12 von 13 im Kurs Rust. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

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);
}

Ausgabe:

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          # Dokumentation

Die 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

CrateZweck
serde, serde_json(De-)Serialisierung
tokioasynchrone Programmierung
reqwestHTTP-Client
clapKommandozeilenargumente
axum, actix-webWebserver
randZufall
regexReguläre Ausdrücke
chronoDatum und Zeit

Merke

  • mod gliedert Code, pub macht öffentlich, use importiert
  • cargo new/run/build/test/fmt/clippy sind die wichtigsten Befehle
  • Cargo.toml listet Abhängigkeiten (Crates von crates.io)
  • Tests: #[test] und cargo test, Makros assert_eq! usw.
  • anyhow/thiserror für Fehler, serde/tokio für den Alltag

Übungsaufgabe

Lege mit cargo new ein Projekt an, schreibe eine Funktion mit zwei Tests und führe cargo test aus.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Smart Pointer, Lifetimes und Threads

Weiter: Referenz und Spickzettel

Alle Kapitel: Rust im Überblick