Невозможно индексировать значение типа `std::fs::ReadDir`

Я пытаюсь получить путь к первой записи в каталоге, но кажется, что тип ответа read_dirReadDir является итератором и не может быть проиндексирован, например.

fn get_single_file_or_none(dir_path: &PathBuf) -> Result<Option<PathBuf>, io::Error> {
    let entries = fs::read_dir(dir_path)?;

    if entries.count() == 1 {
        let path = entries[0].path();
//                        ^^ cannot index into a value of type `std::fs::ReadDir`
        return Ok(path);
    }

    return Ok(None);
}

Как мне получить путь к первой (и в данном случае единственной) записи в каталоге?

Я не могу начать перебирать записи, зная, что они только одни, и вернуть первую, на которую нажал. Это единственное решение? например

if entries.count() == 1 {
    for entry_res in entries {
        return match entry_res {
            Err(er) => Err(er),
            Ok(en) => Ok(Some(en.path())),
        };
    }
}

Имя вашей функции обещает файл, но тело функции не проверяет, действительно ли запись является файлом.

cafce25 01.09.2024 13:02

@cafce25 Спасибо, что выделили. Это был всего лишь быстрый MRE, демонстрирующий ошибку. Как вы заметили, реальная реализация должна проверить, что единственная запись действительно является файлом.

devklick 01.09.2024 13:10
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
2
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете просто вызвать next() один раз на итераторе, чтобы получить первую запись, или None, если записей нет. Звонить count() не обязательно.

use std::{fs, io, path::PathBuf};

fn get_single_file_or_none(dir_path: &PathBuf) -> io::Result<Option<PathBuf>> {
    let mut entries = fs::read_dir(dir_path)?;

    if let Some(entry) = entries.next() {
        return Ok(Some(entry?.path()));
    }

    return Ok(None);
}

Это также можно было бы написать в более функциональном стиле, но из-за комбинации Option и Result это не обязательно будет легче понять...

use std::{fs, io, path::PathBuf};

fn get_single_file_or_none(dir_path: &PathBuf) -> io::Result<Option<PathBuf>> {
    fs::read_dir(dir_path)
        .and_then(|mut entries| entries.next().transpose())
        .map(|entry_opt| entry_opt.map(|entry| entry.path()))
}

Отлично, спасибо! Я искал first() и так и не нашел, но нашел last(), который также мог бы использовать в своем сценарии. Обратите внимание, что ваше решение немного отличается от того, чего я пытаюсь достичь, а именно вернуть путь к единственной записи в каталоге (или None, если ее нет), поэтому проверка count все еще необходима.

devklick 31.08.2024 22:45

@devklick Вы все равно не хотите использовать count(), потому что он прочитает весь итератор, хотя вам нужно знать только, есть ли их 0, 1 или хотя бы 2. Рассмотрим что-то вроде этого: play.rust-lang.org /…

cdhowie 01.09.2024 00:53

Фрэнсис, вы можете включить код из ссылки на игровую площадку в моем комментарии в свой ответ. Он основан на вашем первом фрагменте, но с некоторыми изменениями. (Я объединяю итератор, поскольку ReadDir не объединен, и изменяю логику так, чтобы 2+ элемента заставляли его возвращать None. Вызов предохранителя таков, что если он вернет None, а затем Some(Err(_)), то вместо этого он вернет None и None.)

cdhowie 01.09.2024 00:54
Ответ принят как подходящий

itertools имеет удобную функцию точно_one():

use itertools::Itertools;

fn get_single_file_or_none(dir_path: &PathBuf) -> Result<Option<PathBuf>, io::Error> {
    let entries = fs::read_dir(dir_path)?;
    Ok(entries.exactly_one().ok().transpose()?.map(|it| it.path()))
}

Это более эффективно, чем count(), поскольку каталог будет прочитан только один раз.

Мне нравится это решение, поскольку оно больше соответствует тому, что я пытался сделать. Реализация точно_one очень похожа на то, что cdhowie wa предлагал в одном из других ответов; вызовите next() дважды, убедитесь, что первый возвращает Some, а второй возвращает None

devklick 01.09.2024 10:43

Другие вопросы по теме