Teil 24: Abschlussprojekt fertigstellen
Lernziel: Eine vollständige Anwendung bereitstellen.
Voraussetzungen: Die vorherigen Lektionen, insbesondere Teil 23.
Worum geht es?
In diesem letzten Teil entsteht eine verwendbare Konsolenanwendung. Du verknüpfst Eingaben, Vektoren, Funktionen, Fehlerbehandlung und CSV-Export.
Schritt für Schritt
Vollständiger Quellcode (src/main.rs)
use std::fs;
use std::io::{self, Write};
#[derive(Debug)]
struct Messwert { nummer: usize, temperatur: f64 }
fn frage(text: &str) -> io::Result<String> {
print!("{text}");
io::stdout().flush()?;
let mut eingabe = String::new();
io::stdin().read_line(&mut eingabe)?;
Ok(eingabe.trim().to_string())
}
fn hinzufuegen(werte: &mut Vec<Messwert>, temperatur: f64) {
werte.push(Messwert { nummer: werte.len() + 1, temperatur });
}
fn anzeigen(werte: &[Messwert]) {
if werte.is_empty() { println!("Noch keine Messwerte."); }
for w in werte {
println!("{:>3}: {:>6.1} °C", w.nummer, w.temperatur);
}
}
fn mittelwert(werte: &[Messwert]) -> Option<f64> {
if werte.is_empty() { return None; }
Some(werte.iter().map(|w| w.temperatur).sum::<f64>() / werte.len() as f64)
}
fn exportieren(werte: &[Messwert]) -> io::Result<()> {
let mut csv = String::from("nummer;temperatur
");
for w in werte { csv.push_str(&format!("{};{}
", w.nummer, w.temperatur)); }
fs::write("messwerte.csv", csv)
}
fn main() -> io::Result<()> {
let mut werte: Vec<Messwert> = Vec::new();
loop {
println!("
1 Hinzufügen | 2 Anzeigen | 3 Mittelwert | 4 CSV | 5 Ende");
match frage("Auswahl: ")?.as_str() {
"1" => match frage("Temperatur: ")?.replace(',', ".").parse::<f64>() {
Ok(t) if t.is_finite() => hinzufuegen(&mut werte, t),
_ => println!("Bitte eine gültige endliche Zahl eingeben."),
},
"2" => anzeigen(&werte),
"3" => match mittelwert(&werte) {
Some(m) => println!("Mittelwert: {m:.2} °C"),
None => println!("Keine Messwerte vorhanden."),
},
"4" => match exportieren(&werte) {
Ok(()) => println!("messwerte.csv wurde geschrieben."),
Err(e) => eprintln!("Export fehlgeschlagen: {e}"),
},
"5" => break,
_ => println!("Unbekannte Auswahl."),
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mittelwert_und_leere_liste() {
assert_eq!(mittelwert(&[]), None);
let mut werte = Vec::new();
hinzufuegen(&mut werte, 200.0);
hinzufuegen(&mut werte, 210.0);
assert_eq!(mittelwert(&werte), Some(205.0));
}
}
Test und Release
cargo fmt
cargo clippy
cargo test
cargo run
cargo build --release
Prüfe im Programm die Menüpunkte, eine leere Liste, eine ungültige Eingabe und den CSV-Export. Unter Windows liegt das Release-Programm in target/release/<projektname>.exe, unter Linux ohne .exe.
Ausblick: Nächste Ausbaustufen wären importierbare CSV-Dateien, Zeitstempel, Sensoranbindung oder eine GUI. Diese Themen gehören in einen Aufbaukurs.
Selbst ausprobieren
Erweitere das Programm um einen Menüpunkt 6, der alle gespeicherten Messwerte löscht. Füge einen Test für das Löschen hinzu.
Musterlösung anzeigen
fn loeschen(werte: &mut Vec<Messwert>) { werte.clear(); }
Im Menü 6 Löschen anzeigen und folgenden match-Arm ergänzen: "6" => loeschen(&mut werte),. Ein passender Test: Zwei Werte hinzufügen, loeschen(&mut werte) ausführen und assert!(werte.is_empty()); prüfen.
Das solltest du mitnehmen
- Eine vollständige Anwendung bereitstellen.
- Nutze
cargo runfür das Ausführen undcargo checkfür eine schnelle Fehlerprüfung (ab Teil 4). - Experimentiere mit veränderten Werten und beobachte die Compilermeldungen.
