Я пытаюсь получить путь к первой записи в каталоге, но кажется, что тип ответа read_dir
ReadDir
является итератором и не может быть проиндексирован, например.
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 Спасибо, что выделили. Это был всего лишь быстрый MRE, демонстрирующий ошибку. Как вы заметили, реальная реализация должна проверить, что единственная запись действительно является файлом.
Вы можете просто вызвать 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 Вы все равно не хотите использовать count()
, потому что он прочитает весь итератор, хотя вам нужно знать только, есть ли их 0, 1 или хотя бы 2. Рассмотрим что-то вроде этого: play.rust-lang.org /…
Фрэнсис, вы можете включить код из ссылки на игровую площадку в моем комментарии в свой ответ. Он основан на вашем первом фрагменте, но с некоторыми изменениями. (Я объединяю итератор, поскольку ReadDir
не объединен, и изменяю логику так, чтобы 2+ элемента заставляли его возвращать None
. Вызов предохранителя таков, что если он вернет None
, а затем Some(Err(_))
, то вместо этого он вернет None
и None
.)
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
Имя вашей функции обещает файл, но тело функции не проверяет, действительно ли запись является файлом.