Einleitung: Warum benötigen Sie möglicherweise eine Dateiliste in Rust?
\n\nStellen 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\nDie 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!
1. Vorbereitung: Ein neues Projekt erstellen
\n\nBevor wir Code schreiben, erstellen wir ein neues Binärprojekt. Öffnen Sie das Terminal und führen Sie Folgendes aus:
\n\ncargo 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.
2. Der einfachste Weg: std::fs::read_dir
\n\nDie 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:
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\nWas passiert hier?
\n- \n
fs::read_dir(path)– öffnet das Verzeichnis und gibtReadDir(einen Iterator) zurück. \n - Wir durchlaufen jeden Eintrag. Jeder Aufruf von
next()gibt einResult<DirEntry>zurück – daher istentry?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
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).
2.1. Filtern nach Dateiendung
\n\nOft benötigt man nur bestimmte Dateien, z. B. nur .rs oder .txt. Fügen wir einen Filter hinzu:
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\nDer 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.
3. Rekursive Durchsuchung: In alle Unterordner schauen
\n\nDas 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:
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\nWie 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 ?.
4. Fehlerbehandlung: Was kann schiefgehen?
\n\nIn 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\nuse 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\nDieser Code gibt Fehler auf stderr aus, anstatt das Programm mit panic! zu beenden. Für ein Produktionsskript ist dies viel sicherer.
5. Alternative: Die walkdir-Bibliothek (für komplexe Aufgaben)
\n\nWenn 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:
[dependencies]\nwalkdir = "2"\n\nUnd hier ist der Code:
\n\nuse 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\nwalkdir 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.
Fazit
\n\nWir haben drei Methoden zum Auflisten von Dateien in Rust behandelt:
\n- \n
- Einfach:
std::fs::read_dirfür eine flache Liste. \n - Rekursiv: Eine eigene Funktion mit
std::fs. \n - Professionell: Die
walkdir-Bibliothek für komplexe Aufgaben. \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!