Supposons que je veuille lire tous les fichiers d'un répertoire. Je pourrais définir une fonction comme celle-ci:
use std::path::Path; fn read_all(dir: &Path) -> Result<Vec<String>> { Ok(fs::read_dir(dir)? .filter_map(|entry| read_entry(entry).unwrap()) .collect()) }
Et ensuite essayer de l'utiliser ...
use std::error::Error; use std::fs; use std::io; type Result<T> = std::result::Result<T, Box<dyn Error>>; fn read_entry(entry: io::Result<fs::DirEntry>) -> Result<Option<String>> { let entry = entry?; if entry.file_type()?.is_file() { Ok(Some(fs::read_to_string(entry.file_name())?)) } else { Ok(None) } }
Cela compile mais c'est juste unwrap ()
. collect ()
peut normalement agréger les itérateurs sur les erreurs, mais je ne comprends pas vraiment comment faire cela avec filter_map ()
. Comment puis-je résoudre ce problème?
3 Réponses :
Il y a une belle implémentation de FromIterator
pour Iter
(qui est un trait sous-jacent pour Iterator :: collect code>), donc cela fonctionne:
fn read_all(dir: &Path) -> Result<Vec<String>> {
fs::read_dir(dir)?
.filter_map(|entry| read_entry(entry).transpose())
.collect()
}
Aha transpose ()
était le chaînon manquant! Merci!
Au lieu de renvoyer Result
, vous avez besoin de Option
, c'est-à-dire avant:
Ok (Some (T))
: C'était un fichier et nous l'avons lu avec succès Ok (Aucun)
: Ce n'était pas un fichier mais nous n'avons eu aucune erreur pour le découvrir. Err
: une erreur s'est produite. Après:
Certains (Ok (T))
: C'était un fichier et nous l'avons lu avec succès Aucun
: ce n'était pas un fichier mais nous n'avons eu aucune erreur pour le découvrir. Certains (Err)
: une erreur s'est produite. Vous pouvez modifier l'implémentation d'origine, mais cela signifie que vous ne pouvez pas utiliser l'opérateur ?
, ce qui est nul.
Une meilleure solution - comme l'a souligné @Kitsu est d'utiliser la fonction intégrée transpose ()
qui convertira un Result
en une Option
.
D'accord, de plus je changerais même l'argument read_entry
en entry: fs :: DirEntry
, alors il peut être utilisé sans fermeture supplémentaire, c'est-à-dire .filter_map (read_entry )
Mais read_dir ()
retourne un itérateur sur les Result
s ...?
Juste pour terminer, la raison pour laquelle read_entry
échoue est parce qu'elle utilise entry.file_name ()
au lieu de entry.path ()
.
Donc ceci:
Ok(Some(fs::read_to_string(entry.path())?))
devrait être:
Ok(Some(fs::read_to_string(entry.file_name())?))
Est-ce que cela répond à votre question? Comment éviter de déballer lors de la conversion d'un vecteur d'options ou de résultats uniquement en valeurs réussies?
Non, je ne veux pas ignorer les erreurs.