Avatar ·

Как получить список файлов в директории в Rust?

Всем привет! Я только начал изучать Rust и столкнулся с задачей, которая кажется простой, но вызывает затруднения. Мне нужно прочитать содержимое папки и получить список всех файлов в ней. В Python я бы просто использовал `os.listdir()`, но в Rust всё сложнее. Я попробовал использовать `std::fs::read_dir`, но не совсем понимаю, как правильно обработать результат. Вот мой код: ```rust use std::fs; fn main() { let paths = fs::read_dir(".").unwrap(); for path in paths { println!("{:?}", path); } } ``` Этот код выводит что-то вроде `Ok(DirEntry("./Cargo.toml"))`, а не просто имена файлов. Как мне получить чистый список имён файлов? Нужно ли обрабатывать ошибки для каждого элемента? И как отфильтровать только файлы, исключив папки? Буду благодарен за подробный пример с объяснением, как это сделать правильно в Rust. Желательно с использованием идиоматичного кода, без лишних unwrap'ов. Заранее спасибо! <<>>

Использование std::fs::read_dir с обработкой ошибок

Ваш подход в целом правильный, но требует доработки для идиоматичного Rust. read_dir возвращает итератор, каждый элемент которого — это Result. Чтобы получить только имена файлов, нужно обработать каждый результат, извлечь имя и отфильтровать директории.

use std::fs;
use std::path::Path;

fn list_files(dir: &Path) -> std::io::Result<Vec<String>> {
    let mut files = Vec::new();
    
    for entry in fs::read_dir(dir)? {
        let entry = entry?; // Обрабатываем ошибку доступа к элементу
        let path = entry.path();
        
        // Проверяем, что это файл (не директория)
        if path.is_file() {
            if let Some(name) = path.file_name() {
                if let Some(name_str) = name.to_str() {
                    files.push(name_str.to_string());
                }
            }
        }
    }
    
    Ok(files)
}

fn main() {
    match list_files(Path::new(".")) {
        Ok(files) => {
            for file in files {
                println!("{}", file);
            }
        }
        Err(e) => eprintln!("Ошибка: {}", e),
    }
}

Ключевые моменты:

  • Обработка ошибок: Используйте оператор ? для проброса ошибок вместо unwrap(). Это позволяет вызывающему коду решать, что делать с ошибкой.
  • Извлечение имени: entry.path() возвращает полный путь, file_name() — последний компонент (имя файла). to_str() конвертирует OsStr в &str.
  • Фильтрация: path.is_file() проверяет, что это обычный файл, а не директория или симлинк.
  • Безопасность: Используйте if let Some(...) для обработки случаев, когда имя файла не является валидным UTF-8.

Более краткая версия с filter_map:

use std::fs;
use std::path::Path;

fn list_files_compact(dir: &Path) -> std::io::Result<Vec<String>> {
    let entries = fs::read_dir(dir)?;
    
    Ok(entries
        .filter_map(|entry| entry.ok()) // Игнорируем ошибки доступа
        .filter(|entry| entry.path().is_file())
        .filter_map(|entry| {
            entry.path()
                .file_name()?
                .to_str()
                .map(|s| s.to_string())
        })
        .collect())
}

Этот вариант использует цепочку итераторов: filter_map отбрасывает ошибки и None-значения, filter оставляет только файлы. Результат собирается в Vec<String>.

Рекомендации:

  • Всегда обрабатывайте ошибки — файловая система непредсказуема (нет прав доступа, неверный путь и т.д.).
  • Используйте Path и PathBuf для работы с путями — они кроссплатформенны.
  • Для рекурсивного обхода директорий рассмотрите крейт walkdir.
<<>>

Использование крейта walkdir для рекурсивного обхода

Если вам нужно не только получить список файлов в одной директории, но и обойти все вложенные папки, стандартная библиотека Rust становится неудобной. В этом случае лучшим решением будет крейт walkdir.

Добавьте в Cargo.toml:

[dependencies]
walkdir = "2"

Пример кода для рекурсивного поиска всех файлов:

use walkdir::WalkDir;

fn main() {
    for entry in WalkDir::new(".")
        .into_iter()
        .filter_map(|e| e.ok()) // Игнорируем ошибки
        .filter(|e| e.file_type().is_file()) // Только файлы
    {
        println!("{}", entry.path().display());
    }
}

Преимущества walkdir:

  • Автоматически обрабатывает вложенные директории.
  • Позволяет настроить глубину обхода (.max_depth(2)).
  • Игнорирует скрытые файлы и символические ссылки при необходимости.
  • Более читаемый код для сложных сценариев (например, поиск файлов по расширению).

Для простого случая (только текущая директория) стандартная библиотека достаточна, но для реальных проектов я рекомендую walkdir — он экономит время и уменьшает количество boilerplate-кода.