So listen Sie Dateien in einem Verzeichnis mit Rust auf: Schritt-für-Schritt-Tutorial

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum benötigen Sie möglicherweise eine Dateiliste in Rust?

\n\n

Stellen Sie sich vor: Sie schreiben mit Rust ein Dienstprogramm für die Datensicherung, einen Log-Analysator oder einen einfachen Dateimanager. Und dann steht eine grundlegende, aber kritische Aufgabe an – alle Dateien in einem bestimmten Verzeichnis abrufen. Was könnte daran schon schwierig sein? Aber in Rust, mit seiner strengen Typisierung und dem Besitzsystem, birgt selbst eine so einfache Operation einige Fallstricke.

\n\n

Die Frage „How can I list files of a directory in Rust?“ ist eine der beliebtesten auf Stack Overflow (tausende Aufrufe!). Und das ist kein Zufall: Anfänger stolpern oft über std::fs::read_dir, verwirren sich mit Iteratoren und vergessen die Fehlerbehandlung. In diesem Tutorial zeige ich Ihnen zwei funktionierende Ansätze: einen einfachen (nur Dateien in einem Ordner) und einen fortgeschrittenen (rekursive Durchsuchung aller Unterverzeichnisse). Los geht's!

\n\n

1. Vorbereitung: Ein neues Projekt erstellen

\n\n

Bevor wir Code schreiben, erstellen wir ein neues Binärprojekt. Öffnen Sie das Terminal und führen Sie Folgendes aus:

\n\n
cargo new list_files_rs\ncd list_files_rs
\n\n

Öffnen Sie nun src/main.rs – hier werden wir den gesamten Code schreiben. Für den Anfang benötigen wir keine externen Abhängigkeiten, nur die Standardbibliothek von Rust – std.

\n\n

2. Der einfachste Weg: std::fs::read_dir

\n\n

Die Funktion std::fs::read_dir gibt einen Iterator über die Einträge in einem Verzeichnis zurück. Jeder Eintrag ist ein DirEntry, der den Namen, Pfad, Typ und die Metadaten einer Datei enthält. Schreiben wir ein einfaches Beispiel:

\n\n
use std::fs;\nuse std::path::Path;\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let path = Path::new("."); // aktuelles Verzeichnis\n\n    // Inhalt des Ordners lesen\n    let entries = fs::read_dir(path)?;\n\n    for entry in entries {\n        let entry = entry?; // Result entpacken\n        let path = entry.path();\n\n        // Prüfen, ob es eine Datei ist (kein Verzeichnis)\n        if path.is_file() {\n            println!("{}", path.display());\n        }\n    }\n\n    Ok(())\n}
\n\n

Was passiert hier?

\n
    \n
  • fs::read_dir(path) – öffnet das Verzeichnis und gibt ReadDir (einen Iterator) zurück.
  • \n
  • Wir durchlaufen jeden Eintrag. Jeder Aufruf von next() gibt ein Result<DirEntry> zurück – daher ist entry? erforderlich.
  • \n
  • entry.path() gibt den vollständigen Pfad zur Datei/zum Ordner zurück.
  • \n
  • Die Methode path.is_file() filtert Unterverzeichnisse heraus.
  • \n
\n\n

Führen Sie den Code mit dem Befehl cargo run aus – Sie sehen eine Liste der Dateien im Projektstammverzeichnis (z. B. Cargo.toml, src/main.rs).

\n\n

2.1. Filtern nach Dateiendung

\n\n

Oft benötigt man nur bestimmte Dateien, z. B. nur .rs oder .txt. Fügen wir einen Filter hinzu:

\n\n
use std::fs;\nuse std::path::Path;\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let path = Path::new(".");\n\n    for entry in fs::read_dir(path)? {\n        let entry = entry?;\n        let path = entry.path();\n\n        // Prüfen, ob es eine Datei ist und die Endung .rs hat\n        if path.is_file() && path.extension().map_or(false, |ext| ext == "rs") {\n            println!("{}", path.display());\n        }\n    }\n\n    Ok(())\n}
\n\n

Der Trick mit map_or ist notwendig, um den Fall sicher zu behandeln, dass eine Datei keine Endung hat – path.extension() gibt ein Option<&OsStr> zurück.

\n\n

3. Rekursive Durchsuchung: In alle Unterordner schauen

\n\n

Das einfache read_dir geht nicht in Unterverzeichnisse. Wenn Sie alle Dateien rekursiv sammeln müssen (z. B. zum Suchen von Duplikaten), müssen Sie Ihre eigene rekursive Funktion schreiben. Hier ist eine elegante Lösung:

\n\n
use std::fs;\nuse std::path::Path;\n\nfn visit_dirs(dir: &Path, depth: usize) -> Result<(), Box<dyn std::error::Error>> {\n    if dir.is_dir() {\n        for entry in fs::read_dir(dir)? {\n            let entry = entry?;\n            let path = entry.path();\n            if path.is_dir() {\n                visit_dirs(&path, depth + 1)?;\n            } else {\n                println!("{}", path.display());\n            }\n        }\n    }\n    Ok(())\n}\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let path = Path::new(".");\n    visit_dirs(path, 0)?;\n    Ok(())\n}
\n\n

Wie funktioniert das? Die Funktion visit_dirs ruft sich selbst für jedes gefundene Unterverzeichnis auf. Der Parameter depth ist optional und kann für die Einrückung der Ausgabe verwendet werden. Beachten Sie die Fehlerbehandlung: Jeder read_dir-Aufruf kann fehlschlagen, daher verwenden wir überall ?.

\n\n

4. Fehlerbehandlung: Was kann schiefgehen?

\n\n

In der Praxis können viele Dinge schiefgehen: Die Berechtigungen reichen nicht aus, der Pfad existiert nicht oder es handelt sich um eine Datei statt eines Verzeichnisses. Hier ist eine robustere Version mit detaillierten Fehlermeldungen:

\n\n
use std::fs;\nuse std::path::Path;\n\nfn main() {\n    let path = Path::new(".");\n\n    match fs::read_dir(path) {\n        Ok(entries) => {\n            for entry in entries {\n                match entry {\n                    Ok(entry) => {\n                        let path = entry.path();\n                        if path.is_file() {\n                            println!("{}", path.display());\n                        }\n                    }\n                    Err(e) => eprintln!("Fehler beim Lesen des Eintrags: {}", e),\n                }\n            }\n        }\n        Err(e) => eprintln!("Fehler beim Öffnen des Verzeichnisses: {}", e),\n    }\n}
\n\n

Dieser Code gibt Fehler auf stderr aus, anstatt das Programm mit panic! zu beenden. Für ein Produktionsskript ist dies viel sicherer.

\n\n

5. Alternative: Die walkdir-Bibliothek (für komplexe Aufgaben)

\n\n

Wenn Sie eine fertige und getestete Lösung für die rekursive Durchsuchung benötigen, empfehle ich die walkdir-Bibliothek. Fügen Sie sie zu Cargo.toml hinzu:

\n\n
[dependencies]\nwalkdir = "2"
\n\n

Und hier ist der Code:

\n\n
use walkdir::WalkDir;\n\nfn main() {\n    for entry in WalkDir::new(".") {\n        match entry {\n            Ok(entry) => {\n                if entry.file_type().is_file() {\n                    println!("{}", entry.path().display());\n                }\n            }\n            Err(e) => eprintln!("Fehler: {}", e),\n        }\n    }\n}
\n\n

walkdir erledigt die gesamte Rekursion für Sie, bietet flexible Filter (Tiefe, Sortierung, Ausschluss von Verzeichnissen) und behandelt symbolische Links korrekt. Für die meisten realen Projekte ist dies die beste Wahl.

\n\n

Fazit

\n\n

Wir haben drei Methoden zum Auflisten von Dateien in Rust behandelt:

\n
    \n
  • Einfach: std::fs::read_dir für eine flache Liste.
  • \n
  • Rekursiv: Eine eigene Funktion mit std::fs.
  • \n
  • Professionell: Die walkdir-Bibliothek für komplexe Aufgaben.
  • \n
\n\n

Denken Sie immer an die Fehlerbehandlung – in Rust ist unwrap() nur für schnelle Tests akzeptabel. Verwenden Sie in echtem Code match oder den ?-Operator. Viel Erfolg beim Programmieren!

Blogs

Buchempfehlungen