6 votes

Pourquoi devrais-je préférer `Option::ok_or_else` au lieu de `Option::ok_or` ?

Je viens de voir le changement suivant dans une pull request :

- .ok_or(Error::new(ErrorKind::Other, "Decode error"));
+ .ok_or_else(|| Error::new(ErrorKind::Other, "Decode error"));

Les seules différences que je connais sont les suivantes :

  1. En ok_or nous avons déjà créé Error par Error::new et l'a fait passer dans un adaptateur.
  2. En ok_or_else nous avons passé une fermeture qui produira une telle valeur, mais elle peut ne pas être appelée s'il y a une Some dans la base de données Option .

Ai-je oublié quelque chose ?

13voto

Shepmaster Points 1732

La première raison d'utiliser ok_or_else o tous de la ..._or_else est d'éviter d'exécuter une fonction lorsqu'elle n'est pas nécessaire. Dans le cas des Option::ok_or_else o Option::unwrap_or_else Il n'est donc pas nécessaire d'exécuter un code supplémentaire lorsque l'on utilise la fonction Option est Some . Cela peut rendre le code plus rapide, en fonction de ce qui se passe dans le cas d'erreur

Dans cet exemple, Error::new effectue probablement une allocation, mais il pourrait aussi écrire dans la sortie standard, faire une requête réseau, ou tout ce que n'importe quel morceau de code Rust peut faire ; c'est difficile à dire de l'extérieur. Il est généralement plus sûr de placer ce type de code dans une fermeture afin de ne pas avoir à s'inquiéter des effets secondaires lorsque le cas de réussite se produit.

Clippy le fait également pour vous :

warning: use of `unwrap_or` followed by a function call
 --> src/main.rs:3:15
  |
3 |     let foo = foo.unwrap_or("hello".to_string());
  |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `foo.unwrap_or_else(|| "hello".to_string())`
  |
  = note: #[warn(or_fun_call)] on by default
  = help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#or_fun_call

3voto

red75prime Points 2736

La signature de std::io::Error::new est

fn new<E>(kind: ErrorKind, error: E) -> Error 
where
    E: Into<Box<Error + Send + Sync>>, 

Cela signifie que Error::new(ErrorKind::Other, "Decode error") alloue de la mémoire sur le tas car error doit être converti en Box<Error + Send + Sync> pour être d'une quelconque utilité.

Par conséquent, cette demande d'extension supprime l'allocation/désallocation de mémoire superflue lorsque l'application Result est Result::Ok .

3voto

yzb3 Points 426

Outre les implications en termes de performances, des arguments plus complexes dans les domaines de la santé et de l'environnement peuvent être avancés. ok_or peut donner des résultats inattendus si l'on n'est pas assez prudent ; considérons le cas suivant :

fn main() {
    let value: Option<usize> = Some(1);

    let result = value.ok_or({ println!("value is not Some!"); 0 }); // actually, it is

    assert_eq!(result, Ok(1)); // this holds, but "value is not Some!" was printed
}

Cette situation aurait pu être évitée si ok_or_else (et il en va de même pour les autres *_or_else ), car la fermeture n'est pas évaluée si la variante est Some .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X