diff --git a/README.md b/README.md index d7d5f28264..5c1d2dec83 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,34 @@ -# NOTICE ABOUT STATUS - -The second edition of The Rust Programming Language is getting ever closer to being printed! -This means we're not able to make large changes to chapters that are in any column to the -right of, and including, the "Frozen" column [on our Project board][proj]. Issues or pull -requests submitted for frozen chapters are welcome but will be closed until we start work -on a third edition. Thank you! - -[proj]: https://github.com/rust-lang/book/projects/1 +> **This is a fork of [rust-lang/book](https://github.com/rust-lang/book) in order to translate the English (second-edition) book in French.** +> +> Please read the help [TRANSLATE.md](https://github.com/quadrifoglio/rust-book-fr/blob/master/second-edition/TRANSLATING.md) ! # The Rust Programming Language [![Build Status](https://travis-ci.org/rust-lang/book.svg?branch=master)](https://travis-ci.org/rust-lang/book) -This repo contains two editions of “The Rust Programming Language”; we -recommend starting with the second edition. +This repo contains two editions of “The Rust Programming Language”. -The second edition is a rewrite that will be printed by No Starch Press, -available around May 2018. Check [the No Starch Page][nostarch] for the latest -information on the release date and how to order. +The second edition is a rewrite that will be printed by NoStarch Press, +available around October 2017. -[nostarch]: https://nostarch.com/rust +[You can read it online][html]; the last few chapters aren't completed yet, but +the first half of the book is much improved from the first edition. We recommend +starting with the second edition. -You can read the book for free online! Please see the book as shipped with the -latest [stable], [beta], or [nightly] Rust releases. Be aware that issues in -those versions may have been fixed in this repository already. - -[stable]: https://doc.rust-lang.org/stable/book/second-edition/ -[beta]: https://doc.rust-lang.org/beta/book/second-edition/ -[nightly]: https://doc.rust-lang.org/nightly/book/second-edition/ +[html]: http://rust-lang.github.io/book/ [The first edition is still available to read online][first]. [first]: https://doc.rust-lang.org/book/ - ## Requirements -Building the book requires [mdBook], ideally the same version that -[rust-lang/rust uses in this file][rust-mdbook]. To get it: +Building the book requires [mdBook] >= v0.0.13. To get it: [mdBook]: https://github.com/azerupi/mdBook -[rust-mdbook]: https://github.com/rust-lang/rust/blob/master/src/tools/rustbook/Cargo.toml ```bash -$ cargo install mdbook --vers [version-num] +$ cargo install mdbook ``` ## Building diff --git a/second-edition/TRANSLATING.md b/second-edition/TRANSLATING.md new file mode 100644 index 0000000000..f0ae0b2ce1 --- /dev/null +++ b/second-edition/TRANSLATING.md @@ -0,0 +1,16 @@ +# Translation process + +1. Ask for the access rights to the french fork on the [initial issue](https://github.com/rust-lang/book/issues/808). +2. Choose a chapter to work on, from the ["french translations" board](https://github.com/quadrifoglio/rust-book-fr/projects/1). Make sure that the original writers marked it as "frozen", and +that the source is is up to date with the [original version](https://github.com/rust-lang/book). +3. Please create a [branch](https://github.com/quadrifoglio/rust-book-fr/branches) in order to work on your translation. +4. Translate the chapter. +5. When you are satisfied with your translation, and want feedback/help on your work, please open a [PR](https://github.com/quadrifoglio/rust-book-fr/pulls). + +When the PR is created, you can ask for a review from the other contributors. +You can also help by taking a look at the opened PRs, do not hesitate to comment or provide corrections. + +## Tool suggestions + +- [BonPatron](http://bonpatron.com/) +- [DeepL Translator](https://www.deepl.com/translator) diff --git a/second-edition/src/ch09-00-error-handling.md b/second-edition/src/ch09-00-error-handling.md index 4efd72956e..4679247b2b 100644 --- a/second-edition/src/ch09-00-error-handling.md +++ b/second-edition/src/ch09-00-error-handling.md @@ -1,24 +1,28 @@ -# Error Handling +# Gestion des Erreurs -Rust’s commitment to reliability extends to error handling. Errors are a fact -of life in software, so Rust has a number of features for handling situations -in which something goes wrong. In many cases, Rust requires you to acknowledge -the possibility of an error occurring and take some action before your code -will compile. This requirement makes your program more robust by ensuring that -you’ll discover errors and handle them appropriately before you’ve deployed -your code to production! +L'engagement de Rust envers la fiabilité concerne aussi la gestion des erreurs. +Les erreurs font partie de la vie des programmes informatiques, c'est pourquoi +Rust a des dispositifs pour gérer les situations où les choses se passent mal. +Dans de nombreux cas, Rust exige que vous anticipiez les erreurs possibles et +que vous preniez des dispositions avant de compiler votre code. Cette exigence +rends votre programme plus résiliant en s'assurant que vous détectiez et gérez +les erreurs correctement avant même que vous déployez votre code en production +! -Rust groups errors into two major categories: *recoverable* and *unrecoverable* -errors. Recoverable errors are situations in which it’s reasonable to report -the problem to the user and retry the operation, like a file not found error. -Unrecoverable errors are always symptoms of bugs, like trying to access a -location beyond the end of an array. +Rust classe les erreurs dans deux catégories principales : les erreurs +*récupérables* et *irrécupérables*. Les erreurs récupérables se produisent +dans des situations dans lesquelles il est utile de signaler l'erreur à +l'utilisateur et de relancer l'opération, comme par exemple une erreur lorsque +un fichier n'a pas été trouvé. +Les erreurs irrécupérables sont toujours liées à des bogues, comme essayer +d'accéder à un caractère au-delà de la fin d'un tableau. -Most languages don’t distinguish between these two kinds of errors and handle -both in the same way using mechanisms like exceptions. Rust doesn’t have -exceptions. Instead, it has the type `Result` for recoverable errors and -the `panic!` macro that stops execution when it encounters unrecoverable -errors. This chapter covers calling `panic!` first and then talks about -returning `Result` values. Additionally, we’ll explore considerations to -take into account when deciding whether to try to recover from an error or to -stop execution. +La plupart des langages de programmation ne font pas de distinction entre ces +deux types d'erreurs et les gèrent de la même manière, comme les exceptions. +Rust n'a pas d'exceptions. À la place, il a les valeurs `Result` pour les +erreurs récupérables, et la macro `panic!` qui arrête l'exécution quand il +se heurte à des erreurs irrécupérables. Nous allons commencer ce chapitre par +expliquer l'utilisation de `panic!`, puis ensuite les valeurs de retour +`Result`. De plus, nous allons voir les arguments à prendre en compte +pour choisir si nous devons essayer de récupérer une erreur ou d'arrêter +l'exécution. diff --git a/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md b/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md index c0ba832726..e52c0a8b41 100644 --- a/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md +++ b/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md @@ -1,33 +1,34 @@ -## Unrecoverable Errors with `panic!` - -Sometimes, bad things happen in your code, and there’s nothing you can do about -it. In these cases, Rust has the `panic!` macro. When the `panic!` macro -executes, your program will print a failure message, unwind and clean up the -stack, and then quit. The most common situation this occurs in is when a bug of -some kind has been detected, and it’s not clear to the programmer how to handle -the error. - -> ### Unwinding the Stack or Aborting in Response to a `panic!` -> -> By default, when a `panic!` occurs, the program starts *unwinding*, which -> means Rust walks back up the stack and cleans up the data from each function -> it encounters. But this walking back and cleanup is a lot of work. The -> alternative is to immediately *abort*, which ends the program without -> cleaning up. Memory that the program was using will then need to be cleaned -> up by the operating system. If in your project you need to make the resulting -> binary as small as possible, you can switch from unwinding to aborting on -> panic by adding `panic = 'abort'` to the appropriate `[profile]` sections in -> your *Cargo.toml* file. For example, if you want to abort on panic in release -> mode, add this: -> +## Erreurs Irrécupérables avec `panic!` + +Parfois, les choses se passent mal dans votre code, et vous ne pouvez rien y +faire. Dans ce cas, Rust a la macro `panic!`. Quand la macro `panic!` +s'exécute, votre programme va afficher un message d'erreur, dévide et nettoie +la stack, et ensuite ferme le programme. Le cas le plus courant au cours +duquel cela se produit est quand une bogue a été détecté, et que ce n'est pas +clair pour le développeur de savoir comment gérer cette erreur. + +> ### Dévider la stack ou abandon en réponse à un `panic!` +> Par défaut, quand un `panic!` se produit, le programme commence par +> *dévider*, ce qui veut dire que Rust retourne en arrière dans la stack et +> nettoie les données de chaque fonction qu'il trouve. Mais cette marche en +> arrière et le nettoyage demande beaucoup de ressources. L'alternative est +> d'abandonner *immédiatement* l'exécution, qui arrête le programme sans +> nettoyage. La mémoire qu'utilisait le programme va ensuite devoir être +> nettoyée par le système d'exploitation. Si dans votre projet vous avez besoin +> de construire un exécutable le plus petit possible, vous pouvez passer du +> dévidage à l'abandon lors d'un panic en ajoutant `panic = 'abort'` aux +> sections `[profile]` appropriées dans votre fichier *Cargo.toml*. Par +> exemple, si vous voulez abandonner lors d'un panic en mode release, ajoutez +> ceci : +> > ```toml > [profile.release] > panic = 'abort' > ``` -Let’s try calling `panic!` in a simple program: +Essayons d'appeler `panic!` dans un programme simple : -Filename: src/main.rs +Fichier: src/main.rs ```rust,should_panic fn main() { @@ -35,39 +36,42 @@ fn main() { } ``` -When you run the program, you’ll see something like this: +Quand vous lancez le programme, vous allez voir quelque chose qui ressemble à +cela : ```text $ cargo run Compiling panic v0.1.0 (file:///projects/panic) Finished dev [unoptimized + debuginfo] target(s) in 0.25 secs Running `target/debug/panic` -thread 'main' panicked at 'crash and burn', src/main.rs:2:4 +thread 'main' panicked at 'crash and burn', src/main.rs:2 note: Run with `RUST_BACKTRACE=1` for a backtrace. +error: Process didn't exit successfully: `target/debug/panic` (exit code: 101) ``` -The call to `panic!` causes the error message contained in the last three -lines. The first line shows our panic message and the place in our source code -where the panic occurred: *src/main.rs:2:4* indicates that it’s the second -line, fourth character of our *src/main.rs* file. +L'utilisation de `panic!` déclenche le message d'erreur présent dans les trois +dernières lignes. La première ligne affiche le message associé au panic et +l'emplacement dans notre code source où se produit le panic : *src/main.rs:2* +indique que c'est la ligne 2 de notre fichier *src/main.rs*. -In this case, the line indicated is part of our code, and if we go to that -line, we see the `panic!` macro call. In other cases, the `panic!` call might -be in code that our code calls. The filename and line number reported by the -error message will be someone else’s code where the `panic!` macro is called, -not the line of our code that eventually led to the `panic!` call. We can use -the backtrace of the functions the `panic!` call came from to figure out the -part of our code that is causing the problem. We’ll discuss what a backtrace is -in more detail next. +Dans cet exemple, la ligne indiquée fait partie de notre code, et si nous +allons voir cette ligne, nous verrons l'appel à la macro `panic!`. Dans +d'autres cas, l'appel de `panic!` pourrait se produire dans du code que notre +code utilise. Le nom du fichier et la ligne indiquée par le message d'erreur +sera alors du code de quelqu'un d'autre où la macro `panic!` est appelée, pas +la ligne de notre code qui pourrait mener à cet appel de `panic!`. Nous pouvons +utiliser la backtrace des fonctions qui appellent le `panic!` pour comprendre +la partie de notre code qui pose problème. Nous allons maintenant parler plus +en détail de ce qu'est une backtrace. -### Using a `panic!` Backtrace +### Utiliser la Batrace de `panic!` -Let’s look at another example to see what it’s like when a `panic!` call comes -from a library because of a bug in our code instead of from our code calling -the macro directly. Listing 9-1 has some code that attempts to access an -element by index in a vector: +Analysons un autre exemple pour voir ce qui se passe lors d'un appel de +`panic!` se produit dans un librairie à cause d'un bug dans notre code plutôt +que d'appeler la macro directent. L'entrée 9-1 montre du code qui essaye +d'accéder aux éléments d'un vector via leurs index : -Filename: src/main.rs +Fichier: src/main.rs ```rust,should_panic fn main() { @@ -77,26 +81,27 @@ fn main() { } ``` -Listing 9-1: Attempting to access an element beyond the -end of a vector, which will cause a `panic!` +Entrée 9-1: Tentative d'accéder à un élément en dehors de +la fin d'un vector, ce qui provoque un `panic!` -Here, we’re attempting to access the hundredth element of our vector (which is -at index 99 because indexing starts at zero), but it has only three elements. -In this situation, Rust will panic. Using `[]` is supposed to return an -element, but if you pass an invalid index, there’s no element that Rust could -return here that would be correct. +Ici, nous essayons d'accéder au centième élément (centième car l'indexation +commence à zero) de notre vector, mais il a seulement trois éléments. Dans ce +cas, Rust va faire un panic. Utiliser `[]` est censé retourner un élément, mais +si vous lui fournissez un index invalide, Rust ne pourra pas retourner un +élément acceptable. -Other languages, like C, will attempt to give you exactly what you asked for in -this situation, even though it isn’t what you want: you’ll get whatever is at -the location in memory that would correspond to that element in the vector, -even though the memory doesn’t belong to the vector. This is called a *buffer -overread* and can lead to security vulnerabilities if an attacker is able to -manipulate the index in such a way as to read data they shouldn’t be allowed to -that is stored after the array. +Dans ce cas, d'autres languages, comme le C, vont tenter de vous donner +exactement ce que vous avez demandé, même si ce n'est pas ce que vous voulez : +vous allez récupérer quelque chose à l'emplacement mémoire demandée qui +correspond à l'élément démandé dans le vector, même si cette partie de la +mémoire n'appartient pas au vector. C'est ce qu'on appelle un *buffer overread* +et peut mener à une faille de sécurité si un attaquant à la possibilité de +piloter l'index de telle manière qu'il puisse lire les données qui ne devraient +pas être lisibles en dehors du array. -To protect your program from this sort of vulnerability, if you try to read an -element at an index that doesn’t exist, Rust will stop execution and refuse to -continue. Let’s try it and see: +Afin de protéger votre programme de ce genre de vulnérabilité, si vous essayez +de lire un élément à un index qui n'existe pas, Rust va arrêter l'exécution et +refuser de continuer. Essayez et vous verrez : ```text $ cargo run @@ -104,89 +109,94 @@ $ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs Running `target/debug/panic` thread 'main' panicked at 'index out of bounds: the len is 3 but the index is -99', /checkout/src/liballoc/vec.rs:1555:10 +100', /stable-dist-rustc/build/src/libcollections/vec.rs:1362 note: Run with `RUST_BACKTRACE=1` for a backtrace. error: Process didn't exit successfully: `target/debug/panic` (exit code: 101) ``` -This error points at a file we didn’t write, *vec.rs*. That’s the -implementation of `Vec` in the standard library. The code that gets run when -we use `[]` on our vector `v` is in *vec.rs*, and that is where the `panic!` is -actually happening. - -The next note line tells us that we can set the `RUST_BACKTRACE` environment -variable to get a backtrace of exactly what happened to cause the error. A -*backtrace* is a list of all the functions that have been called to get to this -point. Backtraces in Rust work like they do in other languages: the key to -reading the backtrace is to start from the top and read until you see files you -wrote. That’s the spot where the problem originated. The lines above the lines -mentioning your files are code that your code called; the lines below are code -that called your code. These lines might include core Rust code, standard -library code, or crates that you’re using. Let’s try getting a backtrace: -Listing 9-2 shows output similar to what you’ll see: +Cette erreur se réfère à un fichier que nous n'avons pas codé, +*libcollections/vec.rs*. C'est une implémentation de `Vec` dans la librairie +standard. Le code qui est lancé quand nous utilisons `[]` sur notre vecteur `v` +est dans *libcollections/vec.rs*, et c'est ici que le `panic!` se produit. + +La ligne suivante nous informe que nous pouvons régler la variable +d'environnement `RUST_BACKTRACE` pour obtenir la backtrace de ce qui s'est +exactement passé pour mener à cette erreur. Une *backtrace* est la liste de +toutes les fonctions qui ont été appelées pour arriver jusqu'à ce point. Dans +Rust, la backtrace fonctionne comme elle le fait dans d'autres langages : le +secret pour lire la bracktrace est de commencer d'en haut et lire jusqu'à ce +que vous voyez les fichiers que vous avez écrit. C'est l'emplacement où s'est +produit le problème. Les lignes avant celle qui mentionne vos fichier +représentent le code qu'à appelé votre code; les lignes qui suivent +représentent le code qui a appelé votre code. Ces lignes peuvent être du code +de Rust, du code de la librairie standard, ou des crates que vous utilisez. +Essayons une backtrace: l'entrée 9-2 contient un retour programme similaire à +celui que vous avez provoqué : ```text $ RUST_BACKTRACE=1 cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/panic` -thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /checkout/src/liballoc/vec.rs:1555:10 +thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 100', /stable-dist-rustc/build/src/libcollections/vec.rs:1392 stack backtrace: - 0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace - at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49 - 1: std::sys_common::backtrace::_print - at /checkout/src/libstd/sys_common/backtrace.rs:71 - 2: std::panicking::default_hook::{{closure}} - at /checkout/src/libstd/sys_common/backtrace.rs:60 - at /checkout/src/libstd/panicking.rs:381 - 3: std::panicking::default_hook - at /checkout/src/libstd/panicking.rs:397 - 4: std::panicking::rust_panic_with_hook - at /checkout/src/libstd/panicking.rs:611 - 5: std::panicking::begin_panic - at /checkout/src/libstd/panicking.rs:572 - 6: std::panicking::begin_panic_fmt - at /checkout/src/libstd/panicking.rs:522 - 7: rust_begin_unwind - at /checkout/src/libstd/panicking.rs:498 - 8: core::panicking::panic_fmt - at /checkout/src/libcore/panicking.rs:71 - 9: core::panicking::panic_bounds_check - at /checkout/src/libcore/panicking.rs:58 - 10: as core::ops::index::Index>::index - at /checkout/src/liballoc/vec.rs:1555 - 11: panic::main - at src/main.rs:4 - 12: __rust_maybe_catch_panic - at /checkout/src/libpanic_unwind/lib.rs:99 - 13: std::rt::lang_start - at /checkout/src/libstd/panicking.rs:459 - at /checkout/src/libstd/panic.rs:361 - at /checkout/src/libstd/rt.rs:61 - 14: main - 15: __libc_start_main - 16: + 1: 0x560ed90ec04c - std::sys::imp::backtrace::tracing::imp::write::hf33ae72d0baa11ed + at /stable-dist-rustc/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42 + 2: 0x560ed90ee03e - std::panicking::default_hook::{{closure}}::h59672b733cc6a455 + at /stable-dist-rustc/build/src/libstd/panicking.rs:351 + 3: 0x560ed90edc44 - std::panicking::default_hook::h1670459d2f3f8843 + at /stable-dist-rustc/build/src/libstd/panicking.rs:367 + 4: 0x560ed90ee41b - std::panicking::rust_panic_with_hook::hcf0ddb069e7abcd7 + at /stable-dist-rustc/build/src/libstd/panicking.rs:555 + 5: 0x560ed90ee2b4 - std::panicking::begin_panic::hd6eb68e27bdf6140 + at /stable-dist-rustc/build/src/libstd/panicking.rs:517 + 6: 0x560ed90ee1d9 - std::panicking::begin_panic_fmt::abcd5965948b877f8 + at /stable-dist-rustc/build/src/libstd/panicking.rs:501 + 7: 0x560ed90ee167 - rust_begin_unwind + at /stable-dist-rustc/build/src/libstd/panicking.rs:477 + 8: 0x560ed911401d - core::panicking::panic_fmt::hc0f6d7b2c300cdd9 + at /stable-dist-rustc/build/src/libcore/panicking.rs:69 + 9: 0x560ed9113fc8 - core::panicking::panic_bounds_check::h02a4af86d01b3e96 + at /stable-dist-rustc/build/src/libcore/panicking.rs:56 + 10: 0x560ed90e71c5 - as core::ops::Index>::index::h98abcd4e2a74c41 + at /stable-dist-rustc/build/src/libcollections/vec.rs:1392 + 11: 0x560ed90e727a - panic::main::h5d6b77c20526bc35 + at /home/you/projects/panic/src/main.rs:4 + 12: 0x560ed90f5d6a - __rust_maybe_catch_panic + at /stable-dist-rustc/build/src/libpanic_unwind/lib.rs:98 + 13: 0x560ed90ee926 - std::rt::lang_start::hd7c880a37a646e81 + at /stable-dist-rustc/build/src/libstd/panicking.rs:436 + at /stable-dist-rustc/build/src/libstd/panic.rs:361 + at /stable-dist-rustc/build/src/libstd/rt.rs:57 + 14: 0x560ed90e7302 - main + 15: 0x7f0d53f16400 - __libc_start_main + 16: 0x560ed90e6659 - _start + 17: 0x0 - ``` -Listing 9-2: The backtrace generated by a call to -`panic!` displayed when the environment variable `RUST_BACKTRACE` is set - -That’s a lot of output! The exact output you see might be different depending -on your operating system and Rust version. In order to get backtraces with this -information, debug symbols must be enabled. Debug symbols are enabled by -default when using cargo build or cargo run without the --release flag, as we -have here. - -In the output in Listing 9-2, line 11 of the backtrace points to the line in -our project that’s causing the problem: *src/main.rs* in line 4. If we don’t -want our program to panic, the location pointed to by the first line mentioning -a file we wrote is where we should start investigating to figure out how we got -to this location with values that caused the panic. In Listing 9-1 where we -deliberately wrote code that would panic in order to demonstrate how to use -backtraces, the way to fix the panic is to not request an element at index 99 -from a vector that only contains three items. When your code panics in the -future, you’ll need to figure out what action the code is taking with what -values that causes the panic and what the code should do instead. - -We’ll come back to `panic!` and when we should and should not use `panic!` to -handle error conditions later in the chapter. Next, we’ll look at how to -recover from an error using `Result`. +Entrée 9-2: La backtrace générée par l'appel de `panic!` +est affichée quand la variable d'environnement `RUST_BACKTRACE` est définie + + +Cela fait beaucoup de texte ! L'exactitude de votre retour qui est affiché peut +être différent en fonction de votre système d'exploitation et votre version de +Rust. Pour avoir la backtrace avec ces informations, les instructions de +déboguage doivent être activés. Ils sont activés par défaut quand on utilise +`cargo build` ou `cargo run` sans le flag --release, comme c'est le cas ici. + +Sur la sortie dans l'entrée 9-2, la ligne 11 de la backtrace nous montre la +ligne de notre projet qui provoque le problème : *src/main.rs* à la ligne 4. Si +nous ne voulons pas que notre programme fasse un panic, l'emplacement cité par +la première ligne qui mentionne le code que nous avons écrit est le premier +endroit où nous devrions investiguer pour trouver les valeurs qui ont provoqué +ce panic. Dans l'entrée 9-1 où nous avons délibérément écrit du code qui fait +un panic dans le but de montrer comment nous utilisons les backtrace, la +solution pour ne pas provoquer de panic est de ne pas demander l'élément à +l'index 100 d'un vecteur quand il en contient seulement trois. A l'avenir quand +votre code provoquera des panic, vous aurez besoin de prendre des dispositions +dans votre code avec les valeurs qui provoquent un panic et de coder quoi faire +à la place. + +Nous reviendrons sur le cas du `panic!` et sur les cas où nous devrions et ne +devrions pas utiliser `panic!` pour gérer les conditions d'erreur plus tard +dans ce chapitre. Maintenant, nous allons voir comment gérer une erreur qui +utilise `Result`. diff --git a/second-edition/src/ch09-02-recoverable-errors-with-result.md b/second-edition/src/ch09-02-recoverable-errors-with-result.md index 52e0a689f2..51c0fd600d 100644 --- a/second-edition/src/ch09-02-recoverable-errors-with-result.md +++ b/second-edition/src/ch09-02-recoverable-errors-with-result.md @@ -1,16 +1,17 @@ -## Recoverable Errors with `Result` +## Erreurs récupérables avec `Result` -Most errors aren’t serious enough to require the program to stop entirely. -Sometimes, when a function fails, it’s for a reason that we can easily -interpret and respond to. For example, if we try to open a file and that -operation fails because the file doesn’t exist, we might want to create the -file instead of terminating the process. +La plupart des erreurs ne sont pas assez graves au point d'arrêter complètement +le programme. Parfois, quand une fonction échoue, c'est pour une raison que +nous pouvons facilement comprendre et réagir en conséquence. Par exemple, si +nous essayons d'ouvrir un fichier et que l'opération échoue parce que le +fichier n'existe pas, nous pourrions créer le fichier plutôt que d'arrêter le +processus. -Recall from “[Handling Potential Failure with the `Result` -Type][handle_failure]” in Chapter 2 that the `Result` enum is -defined as having two variants, `Ok` and `Err`, as follows: +Souvenez-vous du Chapitre 2 dans la section “[Gérer les potentielles erreurs +avec `Result`][handle_failure]” quand le enum `Rust` est défini +selon deux variantes, `Ok` et `Err`, comme ci-dessous : -[handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type +[handle_failure]: ch02-00-guessing-game-tutorial.html#gérer-les-potentielles-erreurs-avec-result ```rust enum Result { @@ -19,19 +20,20 @@ enum Result { } ``` -The `T` and `E` are generic type parameters: we’ll discuss generics in more -detail in Chapter 10. What you need to know right now is that `T` represents -the type of the value that will be returned in a success case within the `Ok` -variant, and `E` represents the type of the error that will be returned in a -failure case within the `Err` variant. Because `Result` has these generic type -parameters, we can use the `Result` type and the functions that the standard -library has defined on it in many different situations where the successful -value and error value we want to return may differ. +Le `T` et `E` sont des paramètres de type générique : nous alons parler plus en +détail des génériques au Chapitre 10. Ce que vous avez besoin de savoir pour le +moment c'est que `T` représente le type de valeur nichée dans la variante `Ok` +qui sera retournée dans le cas d'un succès, et `E` représente le type d'erreur +nichée dans la variante `Err` qui sera retournée dans le cas d'un échec. +Puisque `Result` a ces types de paramètres génériques, nous pouvons utiliser le +type `Result` et les fonctions définies dans la librairie standard qui +l'utilisent dans différentes situations où les valeurs en cas de succès et les +valeurs en cas d'erreur que nous attendons en retour peuvent différer. -Let’s call a function that returns a `Result` value because the function could -fail: in Listing 9-3 we try to open a file: +Utilisons une fonction qui retourne une valeur de type `Result` car la fonction +peut échouer : dans l'entrée 9-3 nous essayons d'ouvrir un Nom du fichier : -Filename: src/main.rs +Nom du fichier : src/main.rs ```rust use std::fs::File; @@ -41,21 +43,22 @@ fn main() { } ``` -Listing 9-3: Opening a file +Entrée 9-3 : Ouvrir un fichier -How do we know `File::open` returns a `Result`? We could look at the standard -library API documentation, or we could ask the compiler! If we give `f` a type -annotation of a type that we know the return type of the function is *not* and -then we try to compile the code, the compiler will tell us that the types don’t -match. The error message will then tell us what the type of `f` *is*. Let’s try -it: we know that the return type of `File::open` isn’t of type `u32`, so let’s -change the `let f` statement to this: +Comment savons-nous que `File::open` retourne un `Result` ? Nous pouvons +regarder la documentation de l'API et de la librairie standard, ou nous pouvons +demander au compilateur ! Si nous affectons un type à `f` dont nous savons que +le type de retour de la fonction n'est *pas* correcte et puisque nous essayons +de compiler le code, le compilateur va nous dire que les types ne coïncident +pas. Le message d'erreur va ensuite nous dire de quel type `f` *est*. Essayons +cela : nous savons que le retour de `File::open` n'est pas du type `u32`, alors +essayons de changer l'instruction `let f` comme ceci : ```rust,ignore let f: u32 = File::open("hello.txt"); ``` -Attempting to compile now gives us the following output: +La compilation nous donne maintenant le résultat suivant : ```text error[E0308]: mismatched types @@ -66,33 +69,34 @@ error[E0308]: mismatched types `std::result::Result` | = note: expected type `u32` - found type `std::result::Result` + = note: found type `std::result::Result` ``` -This tells us the return type of the `File::open` function is a `Result`. -The generic parameter `T` has been filled in here with the type of the success -value, `std::fs::File`, which is a file handle. The type of `E` used in the -error value is `std::io::Error`. +Cela nous dit que type de retour de la fonction `File::open` est un +`Result`. Le paramètre générique `T` a été remplacé dans ce cas par le +type en cas de succès, `std::fs::File`, qui est un manipulateur de fichier. Le +type de `E` utilisé pour la valeur d'erreur est `std::io::Error`. -This return type means the call to `File::open` might succeed and return to us -a file handle that we can read from or write to. The function call also might -fail: for example, the file might not exist or we might not have permission to -access the file. The `File::open` function needs to have a way to tell us -whether it succeeded or failed and at the same time give us either the file -handle or error information. This information is exactly what the `Result` enum -conveys. +Ce type de retour veut dire que l'appel à `File::open` peut réussir et nous +retourner un manipulateur de fichier qui peut le lire ou l'écrire. +L'utilisation de cette fonction peut aussi échouer : par exemple, le fichier +peut ne pas exister, ou nous n'avons pas le droit d'accéder au fichier. La +fonction `File::open` doit avoir un moyen de nous dire si son utilisation a +réussi ou échoué et en même temps nous fournir soit le manipulateur de fichier +soit des informations sur l'erreur. C'est exactement ces informations que le +enum `Result` nous transmet. -In the case where `File::open` succeeds, the value we will have in the variable -`f` will be an instance of `Ok` that contains a file handle. In the case where -it fails, the value in `f` will be an instance of `Err` that contains more -information about the kind of error that happened. +Dans le cas où `File::open` réussit, la valeur que nous aurons dans la variable +`f` sera une instance de `Ok` qui contiendra un manipulateur de fichier. Dans +le cas où cela échoue, la valeur dans `f` sera une instance de `Err` qui +contiendra plus d'information sur le type d'erreur qui a eu lieu. -We need to add to the code in Listing 9-3 to take different actions depending -on the value `File::open` returned. Listing 9-4 shows one way to handle the -`Result` using a basic tool: the `match` expression that we discussed in -Chapter 6. +Nous avons besoin d'ajouter différentes actions dans le code de l'entrée 9-3 en +fonction de la valeur que `File::open` a retourné. L'entrée 9-4 montre une +façon de gérer `Result` en utilisant un outil basique : l'expression `match` +que nous avons abordé au Chapitre 6. -Filename: src/main.rs +Nom du fichier : src/main.rs ```rust,should_panic use std::fs::File; @@ -109,41 +113,44 @@ fn main() { } ``` -Listing 9-4: Using a `match` expression to handle the -`Result` variants we might have +Entrée 9-4: Utilisation de l'expression `match` pour +gérer les variantes que `Result` pourrait retourner. -Note that, like the `Option` enum, the `Result` enum and its variants have been -imported in the prelude, so we don’t need to specify `Result::` before the `Ok` -and `Err` variants in the `match` arms. +Veuillez noter que, comme l'enum `Option`, l'enum `Result` et ses variantes ont +été importés dans le prelude, donc vous n'avez pas besoin de préciser +`Result::` avant les variantes `Ok` et `Err` dans le bloc du `match`. -Here we tell Rust that when the result is `Ok`, return the inner `file` value -out of the `Ok` variant, and we then assign that file handle value to the -variable `f`. After the `match`, we can then use the file handle for reading or -writing. +Ici nous indiquons à Rust que quand le résultat est `Ok`, il faut sortir la +valeur `file` de la variante `Ok`, et nous assignons ensuite cette valeur à la +variable `f`. Après le `match`, nous pourrons ensuite utiliser le manipulateur +de fichier pour lire ou écrire. -The other arm of the `match` handles the case where we get an `Err` value from -`File::open`. In this example, we’ve chosen to call the `panic!` macro. If -there’s no file named *hello.txt* in our current directory and we run this -code, we’ll see the following output from the `panic!` macro: +L'autre partie du bloc `match` gère le cas où nous obtenons un `Err` de +l'appel à `File::open`. Dans cet exemple, nous avons choisi d'utiliser la macro +`panic!`. S'il n'y a pas de fichier qui s'appelle *hello.txt* dans notre +répertoire actuel et que nous exécutons ce code, nous allons voir le texte +suivant suite à l'appel de la macro `panic!` : ```text thread 'main' panicked at 'There was a problem opening the file: Error { repr: Os { code: 2, message: "No such file or directory" } }', src/main.rs:9:12 ``` -As usual, this output tells us exactly what has gone wrong. +Comme d'habitude, ce texte nous dit avec précision ce qui s'est mal passé. -### Matching on Different Errors +### Tester les différentes erreurs -The code in Listing 9-4 will `panic!` no matter the reason that `File::open` -failed. What we want to do instead is take different actions for different -failure reasons: if `File::open` failed because the file doesn’t exist, we want -to create the file and return the handle to the new file. If `File::open` -failed for any other reason, for example because we didn’t have permission to -open the file, we still want the code to `panic!` in the same way as it did in -Listing 9-4. Look at Listing 9-5, which adds another arm to the `match`: +Le code dans l'entrée 9-4 va faire un `panic!`, peut importe la raison lorsque +`File::open` échoue. Ce que nous voudrions plutôt faire est de régir +différemment en fonction des cas d'erreurs : si `File::open` a échoué parce que +n'existe pas, nous voulons créer le fichier et renvoyer un manipulateur de +fichier pour ce nouveau fichier. Si `File::open` échoue pour toute autre +raison, par exemple si nous n'avons pas l'autorisation d'ouvrir le fichier, +nous voulons quand même que le code fasse un `panic!` de la même manière qu'il +l'a fait dans l'entrée 9-4. Dans l'entrée 9-5, nous avons ajouté un nouveau cas +au bloc `match` : -Filename: src/main.rs +Nom du fichier : src/main.rs @@ -178,46 +185,53 @@ fn main() { } ``` -Listing 9-5: Handling different kinds of errors in -different ways - -The type of the value that `File::open` returns inside the `Err` variant is -`io::Error`, which is a struct provided by the standard library. This struct -has a method `kind` that we can call to get an `io::ErrorKind` value. -`io::ErrorKind` is an enum provided by the standard library that has variants -representing the different kinds of errors that might result from an `io` -operation. The variant we want to use is `ErrorKind::NotFound`, which indicates -the file we’re trying to open doesn’t exist yet. - -The condition `if error.kind() == ErrorKind::NotFound` is called a *match -guard*: it’s an extra condition on a `match` arm that further refines the arm’s -pattern. This condition must be true for that arm’s code to be run; otherwise, -the pattern matching will move on to consider the next arm in the `match`. The -`ref` in the pattern is needed so `error` is not moved into the guard condition -but is merely referenced by it. The reason `ref` is used to take a reference in -a pattern instead of `&` will be covered in detail in Chapter 18. In short, in -the context of a pattern, `&` matches a reference and gives us its value, but -`ref` matches a value and gives us a reference to it. - -The condition we want to check in the match guard is whether the value returned -by `error.kind()` is the `NotFound` variant of the `ErrorKind` enum. If it is, -we try to create the file with `File::create`. However, because `File::create` -could also fail, we need to add an inner `match` statement as well. When the -file can’t be opened, a different error message will be printed. The last arm -of the outer `match` stays the same so the program panics on any error besides -the missing file error. - -### Shortcuts for Panic on Error: `unwrap` and `expect` - -Using `match` works well enough, but it can be a bit verbose and doesn’t always -communicate intent well. The `Result` type has many helper methods -defined on it to do various tasks. One of those methods, called `unwrap`, is a -shortcut method that is implemented just like the `match` statement we wrote in -Listing 9-4. If the `Result` value is the `Ok` variant, `unwrap` will return -the value inside the `Ok`. If the `Result` is the `Err` variant, `unwrap` will -call the `panic!` macro for us. Here is an example of `unwrap` in action: - -Filename: src/main.rs +Entrée 9-5 : Gestion des différents cas d'erreurs de +avec des actions différentes. + +La valeur de retour de `File::open` nichée dans la variante `Err` est de type +`io::Error`, ce qui est un struct fourni par la librairie standard. Ce struct +a une méthode `kind` que nous pouvons utiliser pour obtenir un retour de type +`io::ErrorKind`. +`io::ErrorKind` est un enum fourni lui aussi par la librairie standard qui a +des variantes qui représentent différents types d'erreurs qui pourraient +résulter d'une opération dans le module `io`. La variante que nous voulons +utiliser est `ErrorKind::NotFound`, qui nous informe que le fichier que nous +essayons d'ouvrir n'existe pas encore. + +La condition `if error.kind() == ErrorKind::NotFound` est ce qu'on appelle un +*match guard* : c'est une condition supplémentaire sur une branche d'un bloc +`match` qui raffine le pattern d'une branche. Cette condition doit être vraie +pour que le code de cette branche soit exécuté; autrement, le pattern matching +s'orientera sur la branche suivante dans le `match`. Le `ref` dans le pattern +est requis pour que `error` ne soit pas déplacée dans dans le conditon du match +guard mais qu'elle soit simplement référencée par elle (NdT: vérifier la trad). +La raison pour laquelle `ref` est utilisé pour stocker une référence dans le +pattern plutôt que un `&` va être expliquée en détails dans le Chapitre 18. +Pour faire court, dans le cas d'un pattern, `&` est associé à une référence et +nous retourne sa valeur, mais `ref` associe une valeur et nous donne une +référence vers elle. + +Le cas que nous voulons vérifier dans le match guard est lorsque la valeur +retournée par `error.kind()` est la variante de `NotFound` de l'enum +`ErrorKind`. Si c'est le cas, nous essayons de créer le fichier avec +`File::create`. Cependant, parce que `File::create` peut aussi échouer, nous +avons besoin d'ajouter à nouveau un `match` à l'intérieur du bloc. Quand le +fichier ne peut pas être ouvert, un message d'erreur différent sera affiché. La +dernière branche du `match` principal reste identique donc le programme fait un +panic sur toute autre erreur que celle du fichier inexistant. + +### Raccourci pour faire un Panic sur une erreur : `unwrap` et `expect` + +L'utilisation de `match` fonctionne assez bien, mais il peut être un peu +verbeux et ne communique pas forcément comme il le faut. Le type `Result` +a de nombreuses méthodes pour nous aider qui lui ont été définies pour faire +plusieurs choses. Une de ces méthodes, qu'on appelle `unwrap`, a été implémenté +comme le `match` que nous avons écri dans l'entrée 9-4 : si la valeur de +`Result` est une variante de `Ok`, `unwrap` va retourner la valeur dans le +`Ok`, et si le `Result` est une variante de `Err`, `unwrap` va appeler la +macro `panic!` pour nous. Voici un exemple de `unwrap` à l'action : + +Nom du fichier : src/main.rs ```rust,should_panic use std::fs::File; @@ -227,21 +241,22 @@ fn main() { } ``` -If we run this code without a *hello.txt* file, we’ll see an error message from -the `panic!` call that the `unwrap` method makes: +Si nous exécutons ce code sans le fichier *hello.txt*, nous allons voir un +message d'erreur d'un appel à `panic!` que la méthode `unwrap` a fait : ```text thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { repr: Os { code: 2, message: "No such file or directory" } }', -src/libcore/result.rs:906:4 +/stable-dist-rustc/build/src/libcore/result.rs:868 ``` -Another method, `expect`, which is similar to `unwrap`, lets us also choose the -`panic!` error message. Using `expect` instead of `unwrap` and providing good -error messages can convey your intent and make tracking down the source of a -panic easier. The syntax of `expect` looks like this: +L'autre méthode, `expect`, qui est similaire à `unwrap`, nous donne la +possibilité de choisir le message d'erreur du `panic!`. Utiliser `expect` +plutôt que `unwrap` et lui fournir de bons messages d'erreurs permet de mieux +exprimer le problème et faciliter la recherche de la source d'erreur. La +syntaxe de `expect` est la suivante : -Filename: src/main.rs +Nom du fichier : src/main.rs ```rust,should_panic use std::fs::File; @@ -251,36 +266,40 @@ fn main() { } ``` -We use `expect` in the same way as `unwrap`: to return the file handle or call -the `panic!` macro. The error message used by `expect` in its call to `panic!` -will be the parameter that we pass to `expect`, rather than the default -`panic!` message that `unwrap` uses. Here’s what it looks like: +Nous utilisons `expect` de la même manière que `unwrap` : pour retourner le +manipulateur de fichier ou appeler la macro `panic!`. Le message d'erreur +utilisé par `expect` lors de son appel au `panic!` sera le paramètre que nous +avons donné à `expect`, plutôt que le message par défaut de `panic!` +qu'utilise `unwrap`. Voici ce que cela donne : ```text thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code: -2, message: "No such file or directory" } }', src/libcore/result.rs:906:4 +2, message: "No such file or directory" } }', +/stable-dist-rustc/build/src/libcore/result.rs:868 ``` -Because this error message starts with the text we specified, `Failed to open -hello.txt`, it will be easier to find where in the code this error message is -coming from. If we use `unwrap` in multiple places, it can take more time to -figure out exactly which `unwrap` is causing the panic because all `unwrap` -calls that panic print the same message. +Parce que ce message d'erreur commence par le texte que nous avons précisé, +`Failed to open hello.txt`, ce sera plus facile de trouver d'où dans le code +ce message d'erreur proviens. Si nous utilisons `unwrap` dans plusieurs +endroits, cela peut prendre plus de temps de comprendre exactement quel +`unwrap` a déclenché le panic, car tous les appels au `unwrap` vont afficher le +même message. -### Propagating Errors +### Propager les Erreurs -When you’re writing a function whose implementation calls something that might -fail, instead of handling the error within this function, you can return the -error to the calling code so that it can decide what to do. This is known as -*propagating* the error and gives more control to the calling code where there -might be more information or logic that dictates how the error should be -handled than what you have available in the context of your code. +Quand vous écrivez une fonction dont son implémentation utilise quelque chose +qui pourrait échouer, plutôt que de gérer l'erreur dans cette fonction, vous +pouvez retourner cette erreur au code qui l'appelle pour qu'il décide quoi +faire. C'est ce qu'on appelle *propager* l'erreur et donne ainsi plus de +pouvoir au code qui appelle la fonction où il pourrait y avoir plus +d'informations ou d'instructions pour traiter l'erreur que si c'était dans le +contexte de votre code. -For example, Listing 9-6 shows a function that reads a username from a file. If -the file doesn’t exist or can’t be read, this function will return those errors -to the code that called this function: +Par exemple, l'entrée 9-6 montre une fonction qui lit un nom d'utilisateur à +partir d'un fichier. Si le fichier n'existe pas ou ne peux pas être lu, cette +fonction va retourner ces erreurs au code qui a appelé cette fonction : -Filename: src/main.rs +Nom du fichier : src/main.rs ```rust use std::io; @@ -304,60 +323,63 @@ fn read_username_from_file() -> Result { } ``` -Listing 9-6: A function that returns errors to the -calling code using `match` - -Let’s look at the return type of the function first: `Result`. This means the function is returning a value of the type -`Result` where the generic parameter `T` has been filled in with the -concrete type `String`, and the generic type `E` has been filled in with the -concrete type `io::Error`. If this function succeeds without any problems, the -code that calls this function will receive an `Ok` value that holds a -`String`—the username that this function read from the file. If this function -encounters any problems, the code that calls this function will receive an -`Err` value that holds an instance of `io::Error` that contains more -information about what the problems were. We chose `io::Error` as the return -type of this function because that happens to be the type of the error value -returned from both of the operations we’re calling in this function’s body that -might fail: the `File::open` function and the `read_to_string` method. - -The body of the function starts by calling the `File::open` function. Then we -handle the `Result` value returned with a `match` similar to the `match` in -Listing 9-4, only instead of calling `panic!` in the `Err` case, we return -early from this function and pass the error value from `File::open` back to the -calling code as this function’s error value. If `File::open` succeeds, we store -the file handle in the variable `f` and continue. - -Then we create a new `String` in variable `s` and call the `read_to_string` -method on the file handle in `f` to read the contents of the file into `s`. The -`read_to_string` method also returns a `Result` because it might fail, even -though `File::open` succeeded. So we need another `match` to handle that -`Result`: if `read_to_string` succeeds, then our function has succeeded, and we -return the username from the file that’s now in `s` wrapped in an `Ok`. If -`read_to_string` fails, we return the error value in the same way that we -returned the error value in the `match` that handled the return value of -`File::open`. However, we don’t need to explicitly say `return`, because this -is the last expression in the function. - -The code that calls this code will then handle getting either an `Ok` value -that contains a username or an `Err` value that contains an `io::Error`. We -don’t know what the calling code will do with those values. If the calling code -gets an `Err` value, it could call `panic!` and crash the program, use a -default username, or look up the username from somewhere other than a file, for -example. We don’t have enough information on what the calling code is actually -trying to do, so we propagate all the success or error information upwards for -it to handle appropriately. - -This pattern of propagating errors is so common in Rust that Rust provides the -question mark operator `?` to make this easier. - -#### A Shortcut for Propagating Errors: `?` - -Listing 9-7 shows an implementation of `read_username_from_file` that has the -same functionality as it had in Listing 9-6, but this implementation uses the -question mark operator: - -Filename: src/main.rs +Entrée 9-6 : une fonction qui retourne les erreurs au +code qui l'appelle en utilisant `match` + +Regardons d'abord le type de retour de la fonction : +`Result`. Cela signifie que la fonction retourne une valeur +de type `Result` où le paramètre générique `T` a été rempli avec le type +`String`, et le paramètre générique `E` a été rempli avec le type `io::Error`. +Si cette fonction réussie sans aucun problème, le code qui appelle cette +fonction va récupérer une valeur `Ok` qui contient un `String`, le nom +d'utilisateur que cette fonction lit dans le fichier. Si cette fonction +rencontre n'importe quel problème, le code qui appelle cette fonction va +récupérer une valeur `Err` qui contient une instance de `io::Error` qui apporte +plus d'informations sur la raison du problème. Nous avons choisi `io::Error` +comme type de retour de cette fonction parce que c'est le type d'erreur de +retour par chacune des opérations qu'on appelle dans le corps de cette fonction +qui peuvent échouer : la fonction `File::open` et la méthode `read_to_string`. + +Le corps de la fonction commence par appeler la fonction `File::open`. Ensuite, +nous gérons la valeur `Result` retourné, avec un `match` similaire au `match` +dans l'entrée 9-4, seulement, au lieu d'appeler `panic!` dans le cas de `Err`, +nous retournons prématurément la fonction et nous retournons la valeur d'erreur +de `File::open` au code appelant avec la valeur d'erreur de cette fonction. Si +`File::open` réussit, nous enregistrons le manipulateur de fichier dans la +variable `f` et nous continuons. + +Ensuite, nous créons un nouveau `String` dans la variable `s` et nous appelons +la méthode `read_to_string` sur le manipulateur de fichier dans `f` pour lire +le contenu du fichier dans `s`. La méthode `read_to_string` retourne aussi un +`Result` parce qu'elle peut échouer, même si `File::open` réussit. Donc nous +avons un nouveau `match` pour gérer ce `Result` : si `read_to_string` réussit, +alors notre fonction a réussi, et nous retournons le nom d'utilisateur à partir +du fichier qui est maintenant dans `s`, enveloppé dans un `Ok`. Si +`read_to_string` échoue, nous retournons la valeur d'erreur de la même façon +que nous avons retourné la valeur d'erreur dans le `match` qui gérait la valeur +de retour de `File::open`. Cependant, nous n'avons pas besoin de dire +explicitement `return`, car c'est la dernière instruction dans la fonction. + +Le code qui appelle ce code va devoir ensuite gérer soit une valeur `Ok` qui +contient le nom d'utilisateur, ou une valeur `Err` qui contient une +`io::Error`. Nous ne savons pas ce que va faire le code appelant avec ces +valeurs. Si le code appelant obtient une valeur `Err`, il peut utiliser un +`panic!` et faire planter le programme, utiliser un nom d'utilisateur par +défaut, ou chercher le nom d'utilisateur autre part que dans ce fichier, par +exemple. Nous n'avons pas assez d'informations sur ce que le code appelant a +l'intention de faire, donc nous remontons toutes les informations de succès ou +d'erreur vers le haut pour qu'elles soient traitées correctement. + +Cette façon de propager les erreurs est si courante dans Rust que Rust fournit +l'opérateur du point d'interrogation `?` pour faciliter ceci. + +#### Un raccourci pour propager les erreurs : `?` + +L'entrée 9-7 montre une implémentation de `read_username_from_file` qui a les +mêmes fonctionnalités qu'elle a dans l'entrée 9-6, mais cette implémentation +utilise l'opérateur du point d'interrogation : + +Nom du fichier : src/main.rs ```rust use std::io; @@ -372,40 +394,43 @@ fn read_username_from_file() -> Result { } ``` -Listing 9-7: A function that returns errors to the -calling code using `?` - -The `?` placed after a `Result` value is defined to work in almost the same way -as the `match` expressions we defined to handle the `Result` values in Listing -9-6. If the value of the `Result` is an `Ok`, the value inside the `Ok` will -get returned from this expression and the program will continue. If the value -is an `Err`, the value inside the `Err` will be returned from the whole -function as if we had used the `return` keyword so the error value gets -propagated to the calling code. - -There is a difference between what the `match` expression from Listing 9-6 and -the question mark operator do: error values used with `?` go through the `from` -function, defined in the `From` trait in the standard library, which is used to -convert errors from one type into another. When the question mark calls the -`from` function, the error type received is converted into the error type -defined in the return type of the current function. This is useful when a -function returns one error type to represent all the ways a function might -fail, even if parts might fail for many different reasons. As long as each -error type implements the `from` function to define how to convert itself to -the returned error type, the question mark operator takes care of the -conversion automatically. - -In the context of Listing 9-7, the `?` at the end of the `File::open` call will -return the value inside an `Ok` to the variable `f`. If an error occurs, `?` -will return early out of the whole function and give any `Err` value to the -calling code. The same thing applies to the `?` at the end of the -`read_to_string` call. - -The `?` eliminates a lot of boilerplate and makes this function’s -implementation simpler. We could even shorten this code further by chaining -method calls immediately after the `?` as shown in Listing 9-8: - -Filename: src/main.rs +Entrée 9-7: une fonction qui retourne les erreurs au code +appelant en utilisant `?` + +Le `?` placé après une valeur `Result` est conçu pour fonctionner presque de la +même manière que la formule `match` que nous avons défini pour gérer les +valeurs `Result` dans l'entrée 9-6. Si la valeur de `Result` est un `Ok`, la +valeur dans le `Ok` sera retournée par cette expression et le programme +continuera. Si la valeur est une `Err`, la valeur à l'intérieur de `Err` sera +retournée par toute la fonction comme si nous avons utilisé le mot-clé `result` +de telle manière que la valeur d'erreur soit propagé au code appelant. + +La seule différence entre l'expression `match` de l'entrée 9-6 et ce que +l'opérateur point d'interrogation fait c'est que lorsque l'on utilise +l'opérateur point d'interrogation, les valeurs d'erreur passent par la fonction +`from` définie dans le trait `From` de la librairie standard. Beaucoup de types +d'erreur implémentent la fonction `from` pour convertir une erreur d'un type +vers une autre erreur d'un autre type. Quand on utilise l'opérateur point +d'interrogation, l'appel de la fonction `from` convertit le type d'erreur que +l'opérateur point d'interrogation obtient vers le type d'erreur défini dans le +type d'erreur de la fonction actuelle où nous utilisons `?`. C'est utile +lorsque des parties de la fonction peuvent échouer pour différentes raisons, +mais que la fonction revoie un type d'erreur qui représente toutes les +possibilités d'échec de la fonction. Du moment que chaque type d'erreur +implémente la fonction `from` pour spécifier comment se convertir dans le type +d'erreur retournée, l'opérateur point d'interrogation s'occupe de faire la +conversion automatiquement. + +Dans le cas de l'entrée 9-7, le `?` à la fin de l'appel à `File::open` va +retourner la valeur à l'intérieur d'un `Ok` à la variable `f`. Si une erreur +survient, `?` va retourner prématurément une valeur `Err` au code appelant. La +même chose se produira au `?` à ma fin de l'appel à `read_to_string`. + +Le `?` épargne du code et facilite l'implémentation. Nous pouvons même encore +plus raccourcir ce code en enchaînant immédiatement les appels aux méthodes +après le `?` comme le montre l'entrée 9-8 : + +Nom du fichier : src/main.rs ```rust use std::io; @@ -421,28 +446,30 @@ fn read_username_from_file() -> Result { } ``` -Listing 9-8: Chaining method calls after the question -mark operator +Entrée 9-8: enchaîner les appels aux méthodes après +l'opérateur point d'interrogation -We’ve moved the creation of the new `String` in `s` to the beginning of the -function; that part hasn’t changed. Instead of creating a variable `f`, we’ve -chained the call to `read_to_string` directly onto the result of -`File::open("hello.txt")?`. We still have a `?` at the end of the -`read_to_string` call, and we still return an `Ok` value containing the -username in `s` when both `File::open` and `read_to_string` succeed rather than -returning errors. The functionality is again the same as in Listing 9-6 and -Listing 9-7; this is just a different, more ergonomic way to write it. +Nous avons déplacé la création du nouveau `String` dans `s` au début de la +fonction; cette partie n'a pas changée. Au lieu de créer la variable `f`, nous +enchaînons directement l'appel à `read_to_string` sur le résultat de +`File::open("hello.txt")?`. Nous avons toujours le `?` à la fin de l'appel à +`read_to_string`, et nous retournons toujours une valeur `Ok` contenant le nom +d'utilisateur dans `s` quand `File::open` et `read_to_string` réussissent tous +les deux plutôt que de retourner des erreurs. Cette fonctionnalité fonctionne +toujours que dans l'entrée 9-6 et l'entrée 9-7; c'est juste une façon +différente et plus ergonomique de l'écrire. -#### `?` Can Only Be Used in Functions That Return Result +#### `?` ne peut être utilisé que dans des fonctions qui retournent `Result` -The `?` can only be used in functions that have a return type of `Result`, -because it is defined to work in the same way as the `match` expression we -defined in Listing 9-6. The part of the `match` that requires a return type of -`Result` is `return Err(e)`, so the return type of the function must be a -`Result` to be compatible with this `return`. +Le `?` peut uniquement être utilisé dans des fonctions qui ont un type de +retour `Result`, car il est défini pour fonctionner de la même manière que +l'expression `match` que nous avons défini dans l'entrée 9-6. La partie du +`match` qui nécessite un type de retour de `Result` est `return Err(e)`, donc +le type de retour de cette fonction doit être `Result` pour être compatible +avec ce `return`. -Let’s look at what happens if we use `?` in the `main` function, which you’ll -recall has a return type of `()`: +Regardons ce que ce passe si nous utilisons `?` dans la fonction `main`, dont +vous devriez vous rappeler qu'elle a un type de retour `()` : ```rust,ignore use std::fs::File; @@ -452,29 +479,30 @@ fn main() { } ``` -When we compile this code, we get the following error message: +Quand nous compilons ce code, nous obtenons le message d'erreur suivant : ```text -error[E0277]: the trait bound `(): std::ops::Try` is not satisfied +error[E0277]: the `?` operator can only be used in a function that returns +`Result` (or another type that implements `std::ops::Try`) --> src/main.rs:4:13 | 4 | let f = File::open("hello.txt")?; | ------------------------ | | - | the `?` operator can only be used in a function that returns - `Result` (or another type that implements `std::ops::Try`) + | cannot use the `?` operator in a function that returns `()` | in this macro invocation | = help: the trait `std::ops::Try` is not implemented for `()` = note: required by `std::ops::Try::from_error` ``` -This error points out that we’re only allowed to use the question mark operator -in a function that returns `Result`. In functions that don’t return `Result`, -when you call other functions that return `Result`, you’ll need to use a -`match` or one of the `Result` methods to handle it instead of using `?` to -potentially propagate the error to the calling code. +Cette erreur explique que nous sommes uniquement autorisés à utiliser +l'opérateur point d'interrogation dans une fonction qui retourne `Result`. Dans +des fonctions qui ne retournent pas `Result`, quand vous utilisez d'autres +fonctions qui retournent `Result`, vous avez besoin d'utiliser `match` ou une +des méthodes de `Result` pour gérer cela plutôt qu'utiliser `?` pour +potentiellement propager l'erreur au code appelant. -Now that we’ve discussed the details of calling `panic!` or returning `Result`, -let’s return to the topic of how to decide which is appropriate to use in which -cases. +Maintenant que nous avons vu les détails pour utiliser `panic!` ou retourner +`Result`, revenons sur au sujet de savoir lequel il convient d'utiliser en +fonction des cas. diff --git a/second-edition/src/ch09-03-to-panic-or-not-to-panic.md b/second-edition/src/ch09-03-to-panic-or-not-to-panic.md index bb710e3c1c..fcfea710cc 100644 --- a/second-edition/src/ch09-03-to-panic-or-not-to-panic.md +++ b/second-edition/src/ch09-03-to-panic-or-not-to-panic.md @@ -1,51 +1,57 @@ ## To `panic!` or Not to `panic!` -So how do you decide when you should `panic!` and when you should return -`Result`? When code panics, there’s no way to recover. You could call `panic!` -for any error situation, whether there’s a possible way to recover or not, but -then you’re making the decision on behalf of the code calling your code that a -situation is unrecoverable. When you choose to return a `Result` value, you -give the calling code options rather than making the decision for it. The -calling code could choose to attempt to recover in a way that’s appropriate for -its situation, or it could decide that an `Err` value in this case is -unrecoverable, so it can call `panic!` and turn your recoverable error into an -unrecoverable one. Therefore, returning `Result` is a good default choice when -you’re defining a function that might fail. - -In a few situations it’s more appropriate to write code that panics instead of -returning a `Result`, but they are less common. Let’s explore why it’s -appropriate to panic in examples, prototype code, and tests; then in situations -where you as a human can know a method won’t fail that the compiler can’t -reason about; and conclude with some general guidelines on how to decide -whether to panic in library code. - -### Examples, Prototype Code, and Tests Are All Places it’s Perfectly Fine to Panic - -When you’re writing an example to illustrate some concept, having robust error -handling code in the example as well can make the example less clear. In -examples, it’s understood that a call to a method like `unwrap` that could -`panic!` is meant as a placeholder for the way that you’d want your application -to handle errors, which can differ based on what the rest of your code is doing. - -Similarly, the `unwrap` and `expect` methods are very handy when prototyping, -before you’re ready to decide how to handle errors. They leave clear markers in -your code for when you’re ready to make your program more robust. - -If a method call fails in a test, we’d want the whole test to fail, even if -that method isn’t the functionality under test. Because `panic!` is how a test -is marked as a failure, calling `unwrap` or `expect` is exactly what should -happen. - -### Cases When You Have More Information Than the Compiler - -It would also be appropriate to call `unwrap` when you have some other logic -that ensures the `Result` will have an `Ok` value, but the logic isn’t -something the compiler understands. You’ll still have a `Result` value that you -need to handle: whatever operation you’re calling still has the possibility of -failing in general, even though it’s logically impossible in your particular -situation. If you can ensure by manually inspecting the code that you’ll never -have an `Err` variant, it’s perfectly acceptable to call `unwrap`. Here’s an -example: +Donc, comment décider quand on doit faire un `panic!` et quand nous devons +retourner un `Result` ? Quand un code fait un panic, il n'y a pas de moyen de +continuer l'exécution. Vous pourriez faire appel à `panic!` pour n'importe +quelle situation d'erreur, peux importe s'il est possible de continuer +l'exécution ou non, mais alors vous prenez la décision de tout arrêter à la +place du code appelant. Quand vous choisissez de retourner une valeur `Result`, +vous donnez plus de choix au code appelant plutôt que si vous preniez des +décisions à sa place. Le code appelant peut choisir d'essayer de récupérer +l'erreur de manière appropriée à cette situation, ou il peut décider que dans +ce cas une valeur `Err` est irrécupérable, donc utiliser `panic!` et changer +votre erreur récupérable en erreur irrécupérable. Ainsi, retourner `Result` est +un bon choix par défaut quand vous construisez une fonction qui peut échouer. + +Dans quelques situations, c'est plus approprié d'écrire du code qui fait un +panic au lieu de retourner un `Result`, mais elles sont moins fréquentes. +Voyons pourquoi il est approprié pourquoi il est plus approprié de faire un +panic dans des exemples, des prototypes, et des tests; ensuite des situations +où vous savez qu'en tant qu'humain qu'une méthode ne peut pas échouer, mais que +le compilateur n'a pas de raison de le faire; et nous allons conclure par +quelques lignes conductrices générales sur comment décider s'il faut paniquer +dans le code des librairies. + +### Les exemples, prototypes, et les tests sont les endroits parfaits pour Panic + +Quand vous écrivez un exemple pour illustrer un concept, avoir un code de +gestion des erreurs très résilient peut rendre l'exemple moins clair. Dans les +exemples, il est courant d'utiliser une méthode comme `unwrap` qui peut faire +un `panic!` qui remplace le code de gestion de l'erreur que vous utiliseriez +dans votre application, qui peut changer en fonction de ce que le reste de +votre code va faire. + +De la même manière, les méthodes `unwrap` et `expect` sont très pratiques pour +coder des prototypes, avant de décider comment gérer les erreurs. Ce sont des +indicateurs claires dans votre code pour plus tard quand vous serez prêt à +rendre votre code plus résilient aux échecs. + +Si l'appel à une méthode échoue dans un test, nous voulons que tout le test +échoue, même si cette méthode n'est pas la fonctionnalité que nous testons. +Parce que `panic!` est la manière de le marquer un échec, utiliser `unwrap` ou +`expect` est exactement ce qui est nécessaire. + +### Les cas où vous avez plus d'informations que le compilateur + +Il peut parfois être approprié d'utiliser `unwrap` quand vous avez un code +logique qui garantie que `Result` aura toujours une valeur de type `Ok`, mais +que c'est une logique que le compilateur ne comprend pas. Vous travaillez +toujours une valeur de type `Result` que vous devez gérer : quel que soit +l'instruction que vous appelez, elle a toujours la possibilité d'échouer, +même si dans ce cas particulier, c'est logiquement impossible. Si vous êtes +sûr en inspectant le code manuellement que vous n'allez jamais obtenir une +variante de `Err`, il est tout à fait acceptable d'utiliser `unwrap`. Voici un +exemple : ```rust use std::net::IpAddr; @@ -53,88 +59,100 @@ use std::net::IpAddr; let home: IpAddr = "127.0.0.1".parse().unwrap(); ``` -We’re creating an `IpAddr` instance by parsing a hardcoded string. We can see -that `127.0.0.1` is a valid IP address, so it’s acceptable to use `unwrap` -here. However, having a hardcoded, valid string doesn’t change the return type -of the `parse` method: we still get a `Result` value, and the compiler will -still make us handle the `Result` as if the `Err` variant is still a -possibility because the compiler isn’t smart enough to see that this string is -always a valid IP address. If the IP address string came from a user rather -than being hardcoded into the program, and therefore *did* have a possibility -of failure, we’d definitely want to handle the `Result` in a more robust way -instead. - -### Guidelines for Error Handling - -It’s advisable to have your code `panic!` when it’s possible that your code -could end up in a bad state. In this context, bad state is when some -assumption, guarantee, contract, or invariant has been broken, such as when -invalid values, contradictory values, or missing values are passed to your -code—plus one or more of the following: - -* The bad state is not something that’s *expected* to happen occasionally. -* Your code after this point needs to rely on not being in this bad state. -* There’s not a good way to encode this information in the types you use. - -If someone calls your code and passes in values that don’t make sense, the best -choice might be to `panic!` and alert the person using your library to the bug -in their code so they can fix it during development. Similarly, `panic!` is -often appropriate if you’re calling external code that is out of your control, -and it returns an invalid state that you have no way of fixing. - -When a bad state is reached, but it’s expected to happen no matter how well you -write your code, it’s still more appropriate to return a `Result` rather than -making a `panic!` call. Examples of this include a parser being given malformed -data or an HTTP request returning a status that indicates you have hit a rate -limit. In these cases, you should indicate that failure is an expected -possibility by returning a `Result` to propagate these bad states upwards so -the calling code can decide how to handle the problem. To `panic!` wouldn’t be -the best way to handle these cases. - -When your code performs operations on values, your code should verify the -values are valid first, and `panic!` if the values aren’t valid. This is mostly -for safety reasons: attempting to operate on invalid data can expose your code -to vulnerabilities. This is the main reason the standard library will `panic!` -if you attempt an out-of-bounds memory access: trying to access memory that -doesn’t belong to the current data structure is a common security problem. -Functions often have *contracts*: their behavior is only guaranteed if the -inputs meet particular requirements. Panicking when the contract is violated -makes sense because a contract violation always indicates a caller-side bug, -and it’s not a kind of error you want the calling code to have to explicitly -handle. In fact, there’s no reasonable way for calling code to recover: the -calling *programmers* need to fix the code. Contracts for a function, -especially when a violation will cause a panic, should be explained in the API -documentation for the function. - -However, having lots of error checks in all of your functions would be verbose -and annoying. Fortunately, you can use Rust’s type system (and thus the type -checking the compiler does) to do many of the checks for you. If your function -has a particular type as a parameter, you can proceed with your code’s logic -knowing that the compiler has already ensured you have a valid value. For -example, if you have a type rather than an `Option`, your program expects to -have *something* rather than *nothing*. Your code then doesn’t have to handle -two cases for the `Some` and `None` variants: it will only have one case for -definitely having a value. Code trying to pass nothing to your function won’t -even compile, so your function doesn’t have to check for that case at runtime. -Another example is using an unsigned integer type like `u32`, which ensures the -parameter is never negative. - -### Creating Custom Types for Validation - -Let’s take the idea of using Rust’s type system to ensure we have a valid value -one step further and look at creating a custom type for validation. Recall the -guessing game in Chapter 2 where our code asked the user to guess a number -between 1 and 100. We never validated that the user’s guess was between those -numbers before checking it against our secret number; we only validated that -the guess was positive. In this case, the consequences were not very dire: our -output of “Too high” or “Too low” would still be correct. It would be a useful -enhancement to guide the user toward valid guesses and have different behavior -when a user guesses a number that’s out of range versus when a user types, for -example, letters instead. - -One way to do this would be to parse the guess as an `i32` instead of only a -`u32` to allow potentially negative numbers, and then add a check for the -number being in range, like so: +Nous créons une instance de `IpAddr` en parsant une chaine de caractères pure. +Nous savons que `127.0.0.1` est une adresse IP valide, donc il est convenable +d'utiliser `unwrap` ici. Toutefois, avoir une chaine de caractères valide, +codée en dur ne change jamais le type de retour de la méthode `parse` : nous +obtenons toujours une valeur de type `Result`, et le compilateur va nous faire +gérer le `Result` comme si la variante `Err` est toujours probable car le +compilateur n'est pas encore suffisamment intelligent pour analyser que cette +chaine de caractères est toujours une adresse IP valide. Si le texte de +l'adresse IP provient de l'utilisateur plutôt que d'être codé en dur dans le +programme, et fait en sorte qu'il y a désormais une possibilité d'erreur, nous +devrions désormais gérer le `Result` de manière plus résiliente. + +### Recommandations pour gérer les erreurs + +Il est recommandé de faire un `panic!` dans votre code lorsqu'il s'exécute dans +de mauvaises conditions. Dans ce sens, les mauvaises conditions sont lorsque un +postulat, une garantie, un contrat ou une invariante a été rompue, comme des +valeurs invalides, contradictoires ou manquantes fournis à votre code, par un +ou plusieurs des éléments suivants : + +* Ces mauvaises conditions ne sont pas *prévues* pour surgir de temps en temps. +* Après cette instruction, votre code a besoin de ne pas être dans ces +mauvaises conditions. +* Il n'y a pas de bonne façon de coder ces informations dans les types que vous +utilisez. + +Si quelqu'un utilise votre code et lui fournit des valeurs qui n'ont pas de +sens, la meilleure des choses à faire et de faire un `panic!` et avertir +le développeur de votre librairie du bogue dans leur code afin qu'il le résoud +pendant la phase de développement. De la même manière, `panic!` est parfois +approprié si vous appelez un code externe dont vous n'avez pas la main dessus, +et qu'il retourne de mauvaises conditions que vous ne pouvez pas corriger. + +Lorsque les conditions sont mauvaises, mais qui est prévu que cela arrive peu +importe la façon dont vous écrivez votre code, il est plus approprié de +retourner un `Result` plutôt que faire appel à `panic!`. Il peut s'agir par +exemple d'un parseur qui reçoit des données erronées, ou une requête HTTP +qui renvoie un statut qui indique que vous avez atteint une limite de débit. +Dans ce cas, vous devriez que cet échec est une possibilité en retournant un +`Result` pour propager ces mauvaises conditions vers le haut pour que le code +appelant puisse décider quoi faire pour gérer le problème. Faire un `panic!` ne +serait pas la manière la plus appropriée ici pour gérer ces problèmes. + +Lorsque votre code effectue des opérations sur des valeurs, votre code devrait +d'abord vérifier que ces valeurs sont valides, et faire un `panic!` si les +valeurs ne sont sont pas correctes. C'est pour essentiellement des raisons de +sécurité : tenter de travailler avec des données invalides peut exposer votre +code à des vulnérabilités. C'est la raison principale pour laquelle la +librairie standard va faire un `panic!` si vous essayez d'accéder d'accéder à +la mémoire en dehors des limites : essayer d'accéder à de la mémoire qui n'a +pas de rapport avec la structure des données actuelle est un problème de +sécurité courant. Les fonctions ont parfois des *contrats* : leur comportement +est garanti uniquement si les données d'entrée remplissent des conditions +particulières. Faire un panic quand le contrat est violé est sensé, car une +violation de contrat indique toujours un bogue du côté de l'appelant, et ce +n'est le genre d'erreur que vous voulez que le code appelant gère +explicitement. En fait, il n'y a aucun moyen raisonnable de récupérer le code +d'appel : le *développeur* du code appelant doit corriger le code. Les contrats +de fonction, en particulier quand une violation va faire un panic, doivent être +expliqués dans la documentation de l'API pour la fonction. + +Cependant, avoir beaucoup de vérifications d'erreurs dans toutes vos fonctions +risque d'être verbeux et ennuyeux. Heureusement, vous pouvez utiliser le +système de type de Rust (et donc la vérification de type du compilateur) pour +faire une partie des vérifications à votre place. Si votre fonction un +paramètre d'un type particulier, vous pouvez continuer à écrire votre code en +sachant que le compilateur s'est déjà assuré que vous avez une valeur valide. +Par exemple, si vous avez un type de valeur plutôt qu'un `Option`, votre +programme s'attend d'avoir *autre chose* plutôt que *rien*. Votre code n'a +donc pas à gérer les deux cas de variantes `Some` et `None` : il n'aura qu'un +seul cas pour avoir une valeur. Du code qui essaye de ne rien fournir à votre +fonction ne compilera même pas, donc votre fonction n'a pas besoin de vérifier +ce cas-ci lors de l'exécution. Un autre exemple est d'utiliser un type unsigned +integer comme `u32`, qui garantit que le paramètre n'est jamais négatif. + +### Créer des types personnalisés pour la Validation + +Allons plus loin dans l'idée d'utiliser le système de types de Rust pour +assurer d'avoir une valeur valide en créant un type personnalisé pour +validation. Souvenez-vous du jeu du plus ou du moins du Chapitre 2 où notre +code demandait à l'utilisateur de deviner un nombre entre 1 et 100. Nous +n'avons jamais validé que le nombre fourni par l'utilisateur était entre ces +nombres avant de le comparer à notre nombre secret; nous avons seulement validé +que le nombre était positif. Dans ce cas, les conséquences ne sont pas très +graves : notre résultat "Too high" ou "Too low" sera toujours correct. Ce +serait une amélioration utile pour guider les suppositions de l'utilisateur +vers des valeurs valides et d'avoir différents comportements quand un +utilisateur propose un nombre en dehors des limites versus quand un utilisateur +renseigne, par exemple, des lettres à la place. + +Une façon de faire cela serait de faire un parse du nombre renseigné en un +`i32` plutôt que seulement un `u32` pour permettre des potentiels nombres +négatifs, et ensuite vérifier que le nombre est dans la plage autorisée, comme +ceci : ```rust,ignore loop { @@ -155,23 +173,25 @@ loop { } ``` -The `if` expression checks whether our value is out of range, tells the user -about the problem, and calls `continue` to start the next iteration of the loop -and ask for another guess. After the `if` expression, we can proceed with the -comparisons between `guess` and the secret number knowing that `guess` is -between 1 and 100. - -However, this is not an ideal solution: if it was absolutely critical that the -program only operated on values between 1 and 100, and it had many functions -with this requirement, it would be tedious (and potentially impact performance) -to have a check like this in every function. - -Instead, we can make a new type and put the validations in a function to create -an instance of the type rather than repeating the validations everywhere. That -way, it’s safe for functions to use the new type in their signatures and -confidently use the values they receive. Listing 9-9 shows one way to define a -`Guess` type that will only create an instance of `Guess` if the `new` function -receives a value between 1 and 100: +L'expression du `if` vérifie si nous valeur est en dehors des limites, et +informe l'utilisateur du problème, et utilise `continue` pour passer à la +prochaine itération de la boucle et demander un nouveau nombre à deviner. Après +l'expression `if`, nous pouvons continuer avec la comparaison entre `guess` et +le nombre secret en sachant que `guess` est entre 1 et 100. + +Cependant, ce n'est pas une solution idéale : si c'était absolument critique +que le programme travaille avec des valeurs entre 1 et 100, et qu'il aurait de +nombreuses fonctions qui exige cela, cela pourrait être fastidieux (et cela +impacterait potentiellement la performance) de faire une validation comme +celle-ci dans chaque fonction. + +A la place, nous pourrions construire un nouveau type et y intégrer les +validations dans une fonction pour créer une instance de ce type plutôt que de +répliquer les validations partout. De cette manière, c'est plus sûr pour les +fonctions d'utiliser le nouveau type dans leurs signatures et d'utiliser avec +confiance les valeurs qu'ils reçoivent. L'entrée 9-9 montre une façon de +définir un type `Guess` qui ne créera une instance de `Guess` uniquement si la +fonction `new` reçoit une valeur entre 1 et 100 : ```rust pub struct Guess { @@ -195,52 +215,56 @@ impl Guess { } ``` -Listing 9-9: A `Guess` type that will only continue with -values between 1 and 100 - -First, we define a struct named `Guess` that has a field named `value` that -holds a `u32`. This is where the number will be stored. - -Then we implement an associated function named `new` on `Guess` that creates -instances of `Guess` values. The `new` function is defined to have one -parameter named `value` of type `u32` and to return a `Guess`. The code in the -body of the `new` function tests `value` to make sure it’s between 1 and 100. -If `value` doesn’t pass this test, we make a `panic!` call, which will alert -the programmer who is writing the calling code that they have a bug they need -to fix, because creating a `Guess` with a `value` outside this range would -violate the contract that `Guess::new` is relying on. The conditions in which -`Guess::new` might panic should be discussed in its public-facing API -documentation; we’ll cover documentation conventions indicating the possibility -of a `panic!` in the API documentation that you create in Chapter 14. If -`value` does pass the test, we create a new `Guess` with its `value` field set -to the `value` parameter and return the `Guess`. - -Next, we implement a method named `value` that borrows `self`, doesn’t have any -other parameters, and returns a `u32`. This is a kind of method sometimes -called a *getter*, because its purpose is to get some data from its fields and -return it. This public method is necessary because the `value` field of the -`Guess` struct is private. It’s important that the `value` field is private so -code using the `Guess` struct is not allowed to set `value` directly: code -outside the module *must* use the `Guess::new` function to create an instance -of `Guess`, which ensures there’s no way for a `Guess` to have a `value` that -hasn’t been checked by the conditions in the `Guess::new` function. - -A function that has a parameter or returns only numbers between 1 and 100 could -then declare in its signature that it takes or returns a `Guess` rather than a -`u32` and wouldn’t need to do any additional checks in its body. - -## Summary - -Rust’s error handling features are designed to help you write more robust code. -The `panic!` macro signals that your program is in a state it can’t handle and -lets you tell the process to stop instead of trying to proceed with invalid or -incorrect values. The `Result` enum uses Rust’s type system to indicate that -operations might fail in a way that your code could recover from. You can use -`Result` to tell code that calls your code that it needs to handle potential -success or failure as well. Using `panic!` and `Result` in the appropriate -situations will make your code more reliable in the face of inevitable problems. - -Now that you’ve seen useful ways that the standard library uses generics with -the `Option` and `Result` enums, we’ll talk about how generics work and how you -can use them in your code in the next chapter. - +Entrée 9-9: un type `Guess` qui ne va continuer +uniquement si la valeur est entre 1 et 100 + +Premièrement, nous définissons un struct qui s'appelle `Guess` qui a un champ +`value` qui stocke un `u32`. C'est là que le nombre sera stocké. + +Ensuite, nous ajoutons une fonction associée `new` sur `Guess` qui crée des +instances de `Guess`. La fonction `new` est conçue pour avoir un paramètre +`value` de type `u32` et de retourner un `Guess`. Le code dans le corps de la +fonction `new` vérifie `value` pour s'assurer que c'est bien entre 1 et 100. +Si `value` échoue à ce test, nous faisons un `panic!`, qui alertera le +développeur qui écrit le code appelant qu'il a un bogue qu'il a besoin de +régler, car créer un `Guess` avec un `value` en dehors de cette plage va violer +le contrat sur lequel `Guess::new` s'appuie. Les conditions dans lesquels +`Guess::new` va faire un panic devrait être explicité dans sa documentation sur +l'API ouverte au public; nous verrons les conventions de documentation pour +indiquer un `panic!` lorsque vous créerez votre documentation d'API au +Chapitre 14. Si `value` réussit le test, nous allons créer un nouveau `Guess` +avec son champ `value` assigné au paramètre `value` et retourner le `Guess`. + +Ensuite, nous implémentons une méthode `value` qui emprunte `self`, qui n'a +aucun autre paramètre, et retourne un `u32`. C'est un type de méthode qui est +parfois appelé un *getter*, car sa fonction est de récupérer (NdT: get) des +données des champs et de les retourner. Cette méthode publique est nécessaire, +car le champ `value` du struct `Guess` est privé. C'est important que le champ +`value` soit privé pour que le code qui utilise la struct `Guess` ne puisse pas +assigner `value` directement : le code en dehors du module *doit* utiliser la +fonction `Guess::new` pour créer une instance de `Guess`, qui s'assure qu'il +n'y a pas de façon pour un `Guess` d'avoir un `value` qui n'a pas été vérifié +par les fonctions dans la fonction `Guess:new`. + +Une fonction qui a un paramètre ou qui retourne des nombres uniquement entre 1 +et 100 peut ensuite utiliser dans sa signature qu'elle prend en paramètre ou +retourne un `Guess` plutôt qu'un `u32` et n'aura pas besoin de faire des +vérifications supplémentaires dans son code. + +## Résumé + +Les fonctionnalités de gestion d'erreurs de Rust sont conçues pour vous aider à +écrire du code plus résilient. La macro `panic!` signale que votre programme +fonctionne dans de mauvaises conditions qu'il ne peut pas gérer et vous permet +de dire au processus de s'arrêter au lieu d'essayer de continuer avec des +valeurs invalides ou incorrectes. Le enum `Result` utilise le système de type +de Rust pour avertir que l'opération peut échouer d'une manière que votre code +pourrait corriger. Vous pouvez utiliser `Result` pour dire au code qui appelle +votre code qu'il a besoin de gérer le résultat et aussi les potentielles +erreurs. Utiliser `panic!` et `Result` de manière appropriée va faire de votre +code plus fiable face à des problèmes inévitables. + +Maintenant que vous avez vu les pratiques utiles que la librairie standard +utilise avec les enums `Option` et `Result`, nous allons voir au chapitre +suivant comment les génériques fonctionnent et comment vous pouvez les utiliser +dans votre code.