web-dev-qa-db-fra.com

Comment puis-je arrêter l'itération et renvoyer une erreur lorsque Iterator :: map renvoie un Result :: Err?

J'ai une fonction qui retourne un Result:

fn find(id: &Id) -> Result<Item, ItemError> {
    // ...
}

Puis un autre l'utilisant comme ceci:

let parent_items: Vec<Item> = parent_ids.iter()
    .map(|id| find(id).unwrap())
    .collect();

Comment gérer le cas d'échec dans l'une des itérations map?

Je sais que je pourrais utiliser flat_map et dans ce cas, les résultats d'erreur seraient ignorés:

let parent_items: Vec<Item> = parent_ids.iter()
    .flat_map(|id| find(id).into_iter())
    .collect();

L'itérateur de Result a 0 ou 1 éléments selon l'état de réussite, et flat_map le filtrera si c'est 0.

Cependant, je ne veux pas ignorer les erreurs, je veux plutôt faire arrêter le bloc de code entier et renvoyer une nouvelle erreur (basée sur l'erreur qui est survenue dans la carte, ou simplement transmettre le erreur existante).

Comment gérer au mieux cela dans Rust?

61
Kai Sellgren

Resultimplémente FromIterator , vous pouvez donc déplacer le Result à l'extérieur et les itérateurs se chargeront du reste (y compris l'arrêt de l'itération si une erreur est trouvée ).

#[derive(Debug)]
struct Item;
type Id = String;

fn find(id: &Id) -> Result<Item, String> {
    Err(format!("Not found: {:?}", id))
}

fn main() {
    let s = |s: &str| s.to_string();
    let ids = vec![s("1"), s("2"), s("3")];

    let items: Result<Vec<_>, _> = ids.iter().map(find).collect();
    println!("Result: {:?}", items);
}

Aire de jeux

75
BurntSushi5

Cette réponse se rapporte à une version antérieure à 1.0 de Rust et les fonctions requises ont été supprimées

Vous pouvez utiliser std::result::fold fonction pour cela. Il cesse d'itérer après avoir rencontré le premier Err.

Un exemple de programme que je viens d'écrire:

fn main() {
  println!("{}", go([1, 2, 3]));
  println!("{}", go([1, -2, 3]));
}

fn go(v: &[int]) -> Result<Vec<int>, String> {
    std::result::fold(
        v.iter().map(|&n| is_positive(n)),
        vec![],
        |mut v, e| {
            v.Push(e);
            v
        })
}

fn is_positive(n: int) -> Result<int, String> {
    if n > 0 {
        Ok(n)
    } else {
        Err(format!("{} is not positive!", n))
    }
}

Production:

Ok([1, 2, 3])
Err(-2 is not positive!)

Démo

1
Dogbert