294 votes

Que signifie ce point d'interrogation pour l'opérateur ?

Je suis en train de lire la documentation pour File :

//..
let mut file = File::create("foo.txt")?;
//..

Quel est le ? dans cette ligne ? Je ne me souviens pas l'avoir vu dans le Livre de la rouille auparavant.

2 votes

Notez que la description de ? a été incluse dans le livre 2018. doc.rust-lang.org/edition-guide/rust-2018/

371voto

Matthieu M. Points 101624

Comme vous l'avez peut-être remarqué, Rust n'a pas d'exceptions. Il a des paniques, mais leur utilisation pour la gestion des erreurs est déconseillée (elles sont destinées aux erreurs irrécupérables).

En Rust, la gestion des erreurs utilise Result . Un exemple typique serait :

fn halves_if_even(i: i32) -> Result<i32, Error> {
    if i % 2 == 0 {
        Ok(i / 2)
    } else {
        Err(/* something */)
    }
}

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = match halves_if_even(i) {
        Ok(i) => i,
        Err(e) => return Err(e),
    };

    // use `i`
}

C'est génial parce que :

  • lorsque vous écrivez le code, vous ne pouvez pas accidentellement oublier de traiter l'erreur,
  • en lisant le code, vous pouvez immédiatement voir qu'il y a un potentiel d'erreur ici.

Il est cependant loin d'être idéal, car il est très verbeux. C'est là que l'opérateur point d'interrogation ? vient.

Ce qui précède peut être réécrit comme suit :

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = halves_if_even(i)?;

    // use `i`
}

qui est beaucoup plus concise.

Quoi ? fait ici est équivalente à la match l'énoncé ci-dessus avec un ajout. En bref :

  1. Il déballe le Result si OK
  2. Il renvoie à l'erreur sinon, l'appel Into::into sur la valeur de l'erreur pour éventuellement la convertir en un autre type.

C'est un peu magique, mais la gestion des erreurs a besoin d'un peu de magie pour réduire le texte passe-partout, et contrairement aux exceptions, il est immédiatement visible quels appels de fonction peuvent ou ne peuvent pas se tromper : ceux qui sont ornés de ? .

Un exemple de la magie est que cela fonctionne également pour Option :

// Assume
// fn halves_if_even(i: i32) -> Option<i32>

fn do_the_thing(i: i32) -> Option<i32> {
    let i = halves_if_even(i)?;

    // use `i`
}

El ? opérateur, stabilisé dans la version Rust 1.13.0 est alimenté par le (instable) Try trait.

Voir aussi :

10 votes

Il serait bien que vous étendiez un peu votre réponse, par exemple en expliquant que le type de retour de la fonction doit correspondre au type que vous essayez de "déballer", par exemple Result o Option .

0 votes

Je pense que ce serait mieux de poser une toute nouvelle question.

0 votes

En ce qui concerne les paniques transportant des informations structurées, elles le peuvent désormais grâce à panic_any .

32voto

snnsnn Points 81

C'est pour la propagation d'erreur pour le type d'erreur récupérable Result<T, E> . Il déballe le résultat et vous donne la valeur intérieure.

Plutôt que de traiter le cas d'erreur, vous le propagez au code de l'appelant et ne traitez que le Ok cas. L'avantage est qu'il élimine une grande partie du texte passe-partout et rend l'implémentation de la fonction plus simple.

26 votes

À ne pas confondre avec la réalité .unwrap() qui panique en cas d'erreur.

13voto

Yilmaz Points 51

Il est utilisé pour propagating errors . Parfois, nous écrivons du code qui peut échouer mais nous ne voulons pas attraper et traiter l'erreur immédiatement. Votre code ne sera pas lisible si vous avez trop de code pour traiter l'erreur à chaque endroit. Au lieu de cela, si une erreur se produit, nous pouvons vouloir laisser notre appelant s'en occuper. Nous voulons que les erreurs se propagent dans la pile d'appel.

 let mut file = File::create("foo.txt")?;

Le comportement de ? dépend du fait que cette fonction renvoie un résultat réussi ou un résultat d'erreur :

  • S'il s'agit d'un succès, il déballe le résultat pour obtenir la valeur du succès à l'intérieur.
  • En cas d'erreur, elle retourne immédiatement à la fonction englobante, en transmettant le résultat de l'erreur en amont de la chaîne d'appel. Pour s'assurer que cela fonctionne, ? ne peut être utilisé sur un résultat que dans les fonctions qui ont un type de retour Résultat.

Utilisation de ? comme ce code

let mut file = match File::create("foo.txt") {
        Err(why) => panic!("couldn't create {}: {}", display, why),
        Ok(file) => file,
    };

? fonctionne également de manière similaire avec le type Option. Dans une fonction qui renvoie Option, vous pouvez utiliser ? pour déballer une valeur et la retourner au début dans le cas de None :

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