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.