diff --git a/.travis.yml b/.travis.yml index be8ed09bda..d8247288d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: trusty language: rust cache: cargo rust: - - 1.31.1 + - 1.37.0 branches: only: - master diff --git a/.vscode/settings.json b/.vscode/settings.json index 3905ae4c1d..4a964f40f7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,19 @@ { - "editor.rulers": [80] -} \ No newline at end of file + "editor.rulers": [80], + "markdownlint.config": { + "MD001": false, + "MD002": false, + "MD007": false, + "MD013": { + "headings": false, + "tables": false, + "code_blocks": false + }, + "MD014": false, + "MD026": false, + "MD033": { + "allowed_elements": ["img", "span", "sup"] + }, + "MD041": false + } +} diff --git a/Cargo.lock b/Cargo.lock index 928f6b1244..dbf955717c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "aho-corasick" version = "0.5.3" diff --git a/FRENCH/CONTRIBUTING.md b/FRENCH/CONTRIBUTING.md index ef5b0d1787..6650b71d95 100644 --- a/FRENCH/CONTRIBUTING.md +++ b/FRENCH/CONTRIBUTING.md @@ -9,53 +9,69 @@ We need to coordinate us with the main English terms translations. In this purpose, please refer to the file `/FRENCH/src/translation-terms.md` when you need to translate a technicial term. -*(PS : see next Process `Add a translation term` on this same page)* +*(PS : see the next process `Add a translation term` on this same page)* ### Translation of Rust code -We should translate only the comments and the string text in the rust code. +In rust code, we should translate : -All the remaining code (and terminal outputs) should stay in English (variables, -methods/functions, instructions, ...). +- comment +- string text +- variable name +- struct name (which is custom, that means it not come from *standard library* + or *external crate*) +- enum name (which is custom, that means it not come from *standard library* + or *external crate*) + +All the standard code (and terminal outputs) should stay in English. ### Files -The name of all the files should not be translated, so just keep them in -English. +The name of all the files should not be translated, so just keep them unchanged, +in English. Please limit each line of Markdown file to 80 characters (including spaces). You can write your file as you want, but it would be nice to use a tool like [https://www.dcode.fr/text-splitter](https://www.dcode.fr/text-splitter) on your translated paragraphs before commiting. +### Punctuation + +Please use a [non-breaking space](https://en.wikipedia.org/wiki/Non-breaking_space) +instead of space on punctuation who need this space before (like `:`, `!`, `?`, +...). + ## Processes ### Translate flow -*NB : the following `main translate repository` refers to -https://github.com/Jimskapt/rust-book-fr* +*NB : the following term `main translate repository` refers to +* 01. Open or edit an GitHub issue in the *main translate repository* to report to other that you are working on `ch00-00-introduction`. - if someone has reported to work on the same page you want to translate : - if its works is *active* : you should work on another page. - - else : you can fork its repo in the following step 02 (instead of the - *main translate repository*), please mention it in the existing issue. + - else : you can fork his/her repo in the following step 02 (instead of + the *main translate repository*), and please mention this in the + existing issue. - else : just follow the next instructions. -02. Fork the *main translate repository* in GitHub (in your account) +02. Fork the *main translate repository* in GitHub (on your account) 03. `git clone https://github.com/.git` (copy your fork on your hard-disk). You should be on the `french-release` branch by default. -04. `cd ` (go inside your fork folder) +04. `cd ` (go inside your fork folder your previously downloaded) 05. `git checkout -b ` (create your new working branch) 06. Copy the file you want to translate : `/src/ch00-00-introduction.md` into - `/FRENCH/src/ch00-00-introduction.md` + `/FRENCH/src/ch00-00-introduction.md`. Tip : you can use a tool like + [Castor Whispers](https://github.com/Jimskapt/castor_whispers) in order to + copy and mass-comment each paragraphs of this file. 07. Add the text for the link to this file in the `/FRENCH/src/SUMMARY.md`. 08. Comment each English paragraphs of this file. The goal is to keep an *invisible* version of the English book, in order to easily reflect the changes of the English source (english-book) when they occur later, and to - translate them. + easily translate them. 09. Write each of your translated paragraph under each commented English paragraph. - Please quickly read following `Guidelines` currently on this page. @@ -64,9 +80,9 @@ https://github.com/Jimskapt/rust-book-fr* tool like [https://www.dcode.fr/text-splitter](https://www.dcode.fr/text-splitter). 11. (optionnal) `cd FRENCH && mdbook build && cd ..` (build the book in - `/FRENCH/book`). Open its index.html file, and check its correctness. It - also should help you for next task. -12. (optionnal) proofreading you work thanks to services like + `/FRENCH/book`). Open its index.html file in your browser, and check its + correctness. It also should help you for next task. +12. (optionnal) self-proofreading your work thank to services like [bonpatron.fr](https://bonpatron.com). 13. `git add -A && git commit -m ""` (committing your work) @@ -75,27 +91,44 @@ https://github.com/Jimskapt/rust-book-fr* 15. `git push origin` (pushing your work on your fork) 16. In GitHub, create a new pull request from your fork to the main translation repository, in order to mark your work ready for a proofreading. -17. After proofreading (and eventualy some edits from others), it would be +17. After someone proofreading it (and eventualy some edits), it would be merged on `french-release` branch. -### Update from English book +### Update your fork with another fork -01. `git remote add --track master english-book https://github.com/rust-lang/book.git` +01. `git remote add english-book https://github.com/rust-lang/book.git` (Add source of the *English main repository*) 02. `git fetch english-book` (fetching the latest changes from the *English main repository*) 03. `git merge english-book/master` (merging latest changes from *English main repository* on current branch) +It is also the same to update your fork with the main translate repository. + ### Add a translation term *(PS : see previous Guideline `Translation terms` on this same page)* -01. `git checkout -b ` (create your new working - branch) +01. Check if you are one your working branch, or create it (see `Translate flow` + process) 02. Edit the `/FRENCH/src/translation-terms.md` file with your new technical - term translation. -03. Then do a *Push Request (PR)* in order discuss it with the team. -04. While PR is being accepted and merged, you would better use the English word - in your work instead of your translation, and then edit it when the team is - OK with the translation. + term translation. Write it in singular and if necessary, specify the gender + of the translation in `Remarques` column. + +### Translate figures + +Let's suppose you want to translate Figure 42-69. +You need to have the `dot` installed, for instance after typing +`sudo apt install graphviz`. + +01. Copy the DOT figure you want to translate (for instance, `trpl42-69.dot`) + from `/dot/` to `/FRENCH/dot/`. +02. Edit `/FRENCH/dot/trpl42-69.dot` and translate the text into French. + You should not translate the names and values of attributes. +03. Run `dot FRENCH/dot/trpl42-69.dot -Tsvg > FRENCH/src/img/trpl42-69.svg` +04. Edit the new file `FRENCH/src/img/trpl42-69.svg`: + - Within the `` tag, remove the `width` and `height` attributes, and + set the `viewBox` attribute to `0.00 0.00 1000.00 1000.00` or other values + that don't cut off the image. + - Replace every instance of `font-family="Times,serif"` with + `font-family="Times,Liberation Serif,serif"`. diff --git a/FRENCH/dot/trpl04-01.dot b/FRENCH/dot/trpl04-01.dot new file mode 100644 index 0000000000..ca90ee3e16 --- /dev/null +++ b/FRENCH/dot/trpl04-01.dot @@ -0,0 +1,26 @@ +digraph { + rankdir=LR; + overlap=false; + dpi=300.0; + node [shape="plaintext"]; + + table0[label=< + + + + + +
s1
nomvaleur
pointeur
taille5
capacité5
>]; + table1[label=< + + + + + + +
indicevaleur
0h
1e
2l
3l
4o
>]; + + edge[tailclip="false"]; + table0:pointer:c -> table1:pointee; +} + diff --git a/FRENCH/dot/trpl04-02.dot b/FRENCH/dot/trpl04-02.dot new file mode 100644 index 0000000000..2933b9b799 --- /dev/null +++ b/FRENCH/dot/trpl04-02.dot @@ -0,0 +1,35 @@ +digraph { + rankdir=LR; + overlap=false; + dpi=300.0; + node [shape="plaintext"]; + + table0[label=< + + + + + +
s1
nomvaleur
pointeur
taille5
capacité5
>]; + table3[label=< + + + + + +
s2
nomvaleur
pointeur
taille5
capacité5
>]; + + table1[label=< + + + + + + +
indicevaleur
0h
1e
2l
3l
4o
>]; + + edge[tailclip="false"]; + table0:pointer:c -> table1:pointee; + table3:pointer:c -> table1:pointee; +} + diff --git a/FRENCH/dot/trpl04-03.dot b/FRENCH/dot/trpl04-03.dot new file mode 100644 index 0000000000..48d9082e2f --- /dev/null +++ b/FRENCH/dot/trpl04-03.dot @@ -0,0 +1,44 @@ +digraph { + rankdir=LR; + overlap=false; + dpi=300.0; + node [shape="plaintext"]; + + table0[label=< + + + + + +
s2
nomvaleur
pointeur
taille5
capacité5
>]; + table1[label=< + + + + + + +
indicevaleur
0h
1e
2l
3l
4o
>]; + + table3[label=< + + + + + +
s1
nomvaleur
pointeur
taille5
capacité5
>]; + table4[label=< + + + + + + +
indicevaleur
0h
1e
2l
3l
4o
>]; + + + edge[tailclip="false"]; + table0:pointer:c -> table1:pointee; + table3:pointer:c -> table4:pointee; +} + diff --git a/FRENCH/dot/trpl04-04.dot b/FRENCH/dot/trpl04-04.dot new file mode 100644 index 0000000000..de16ad024c --- /dev/null +++ b/FRENCH/dot/trpl04-04.dot @@ -0,0 +1,35 @@ +digraph { + rankdir=LR; + overlap=false; + dpi=300.0; + node [shape="plaintext"]; + + table0[label=< + + + + + +
s1
nomvaleur
pointeur
taille5
capacité5
>]; + table3[label=< + + + + + +
s2
nomvaleur
pointeur
taille5
capacité5
>]; + + table1[label=< + + + + + + +
indicevaleur
0h
1e
2l
3l
4o
>]; + + edge[tailclip="false"]; + table0:pointer:c -> table1:pointee; + table3:pointer:c -> table1:pointee; +} + diff --git a/FRENCH/dot/trpl04-05.dot b/FRENCH/dot/trpl04-05.dot new file mode 100644 index 0000000000..f02f13f936 --- /dev/null +++ b/FRENCH/dot/trpl04-05.dot @@ -0,0 +1,32 @@ +digraph { + rankdir=LR; + overlap=false; + dpi=300.0; + node [shape="plaintext"]; + + table0[label=< + + + +
s
nomvaleur
pointeur
>]; + table1[label=< + + + + + +
s1
nomvaleur
pointeur
taille5
capacité5
>]; + table2[label=< + + + + + + +
indicevaleur
0h
1e
2l
3l
4o
>]; + + edge[tailclip="false"]; + table1:pointer:c -> table2:pointee; + table0:borrower:c -> table1:borrowee; +} + diff --git a/FRENCH/dot/trpl04-06.dot b/FRENCH/dot/trpl04-06.dot new file mode 100644 index 0000000000..cc5d7fb438 --- /dev/null +++ b/FRENCH/dot/trpl04-06.dot @@ -0,0 +1,41 @@ +digraph { + rankdir=LR; + overlap=false; + dpi=300.0; + node [shape="plaintext"]; + + table0[label=< + + + + +
world
nomvaleur
pointeur
taille5
>]; + + table3[label=< + + + + + +
s
nomvaleur
pointeur
taille11
capacité11
>]; + table4[label=< + + + + + + + + + + + + +
indicevaleur
0h
1e
2l
3l
4o
5
6w
7o
8r
9l
10d
>]; + + + edge[tailclip="false"]; + table0:pointer2:c -> table4:pointee2; + table3:pointer:c -> table4:pointee; +} + diff --git a/FRENCH/src/SUMMARY.md b/FRENCH/src/SUMMARY.md index 4582e6ac31..95e39c60b0 100644 --- a/FRENCH/src/SUMMARY.md +++ b/FRENCH/src/SUMMARY.md @@ -14,4 +14,18 @@ - [Programmer un jeu de devinettes](ch02-00-guessing-game-tutorial.md) -- [Le type de découpage](ch04-03-slices.md) +- [Les concepts courants de programmation](ch03-00-common-programming-concepts.md) + - [Les variables et la mutabilité](ch03-01-variables-and-mutability.md) + - [Les types de données](ch03-02-data-types.md) + - [Les fonctions](ch03-03-how-functions-work.md) + - [Les commentaires](ch03-04-comments.md) + - [Les structures de contrôle](ch03-05-control-flow.md) + +- [Comprendre la possession](ch04-00-understanding-ownership.md) + - [Qu'est-ce que la possession ?](ch04-01-what-is-ownership.md) + - [Les références et l'emprunt](ch04-02-references-and-borrowing.md) + - [Le type slice](ch04-03-slices.md) + +- [Utiliser les structures pour structurer des données apparentées](ch05-00-structs.md) + - [Définir et instancier des structures](ch05-01-defining-structs.md) + - [Un exemple de programme qui utilise des structures](ch05-02-example-structs.md) diff --git a/FRENCH/src/ch00-00-introduction.md b/FRENCH/src/ch00-00-introduction.md index 071845d6ca..7e1e09db70 100644 --- a/FRENCH/src/ch00-00-introduction.md +++ b/FRENCH/src/ch00-00-introduction.md @@ -272,7 +272,7 @@ avez appris précédemment. Les chapitres 2, 12 et 20 sont des chapitres de projet ; les autres sont des chapitres théoriques. + | Ferris | Signification | |------------------------------------------------------------------------|--------------------------------------------------| diff --git a/FRENCH/src/ch01-01-installation.md b/FRENCH/src/ch01-01-installation.md index 609f6ed0ad..5edcfd5c8f 100644 --- a/FRENCH/src/ch01-01-installation.md +++ b/FRENCH/src/ch01-01-installation.md @@ -176,12 +176,16 @@ pour Visual Studio 2013 ou plus récent. La méthode la plus facile pour obtenir les outils de compilation est d'installer [Build Tools pour Visual Studio 2019][visualstudio]. -[install]: https://www.rust-lang.org/tools/install + + + +[install]: https://www.rust-lang.org/tools/install [visualstudio]: https://www.visualstudio.com/fr/downloads/#build-tools-for-visual-studio-2019 -L'installateur embarque aussi une copie de la documentation en local pour que -vous puissiez la lire hors ligne. Lancez `rustup doc` afin -d'ouvrir la documentation locale dans votre navigateur. +L'installation de Rust embarque aussi une copie de la documentation en local +pour que vous puissiez la lire hors ligne. Lancez `rustup doc` afin d'ouvrir la +documentation locale dans votre navigateur. Ouvrez un terminal et écrivez les commandes suivantes pour créer un -dossier *projects* et un dossier pour le projet *Hello, world!* à l'intérieur +dossier *projects* et un dossier pour le projet “Hello, world!” à l'intérieur de ce dossier *projects*. -Regardons en détail ce qui s'est passé dans votre programme *Hello, world!*. +Regardons en détail ce qui s'est passé dans votre programme “Hello, world!”. Voici le premier morceau du puzzle : ```rust @@ -394,11 +393,11 @@ $ ./main # ou .\main.exe sous Windows ``` -Si *main.rs* était votre programme *Hello, world!*, cette ligne devrait afficher +Si *main.rs* était votre programme “Hello, world!”, cette ligne devrait afficher `Hello, world!` dans votre terminal. Des programmes Rust très simples, comme le petit que nous avons précédemment, -n'ont pas de dépendance. Donc si nous avions compilé le projet *Hello, world!* +n'ont pas de dépendance. Donc si nous avions compilé le projet “Hello, world!” avec Cargo, cela n'aurait fait appel qu'à la fonctionnalité de Cargo qui s'occupe de la compilation de votre code. Quand vous écrirez des programmes Rust plus complexes, vous ajouterez des dépendances, et si vous créez un projet en @@ -71,13 +71,13 @@ séparément Cargo. Créons un nouveau projet en utilisant Cargo et analysons les différences avec -notre projet initial *Hello, world!*. Retournez dans votre dossier *projects* +notre projet initial “Hello, world!”. Retournez dans votre dossier *projects* (ou là où vous avez décidé d'enregistrer votre code). Ensuite, sur n'importe quel système d'exploitation, lancez les commandes suivantes : @@ -220,13 +220,13 @@ fn main() { ``` -Cargo a généré un programme *Hello, world!* pour vous, exactement comme celui +Cargo a généré un programme “Hello, world!” pour vous, exactement comme celui que nous avons écrit dans l'encart 1-1 ! Pour le moment, les seules différences entre notre projet précédent et le projet que Cargo a généré sont que Cargo a placé le code dans le dossier *src*, et que nous avons un fichier de @@ -247,14 +247,14 @@ pas directement relié à votre code. Utiliser Cargo vous aide à structurer vos projets. Il y a un endroit pour tout, et tout est à sa place. Si vous commencez un projet sans utiliser Cargo, comme nous l'avons fait avec -le projet *Hello, world!*, vous pouvez le transformer en projet qui +le projet “Hello, world!”, vous pouvez le transformer en projet qui utilise Cargo. Déplacez le code de votre projet dans un dossier *src* et créez un fichier *Cargo.toml* adéquat. @@ -265,13 +265,13 @@ un fichier *Cargo.toml* adéquat. ### Compiler et exécuter un projet Cargo Maintenant, regardons ce qu'il y a de différent quand nous compilons et -exécutons le programme *Hello, world!* avec Cargo ! À l'intérieur de votre +exécutons le programme “Hello, world!” avec Cargo ! À l'intérieur de votre dossier *hello_cargo*, compilez votre projet en utilisant la commande suivante : ```text @@ -524,14 +524,14 @@ avez appris comment : * Install the latest stable version of Rust using `rustup` * Update to a newer Rust version * Open locally installed documentation -* Write and run a Hello, world! program using `rustc` directly +* Write and run a “Hello, world!” program using `rustc` directly * Create and run a new project using the conventions of Cargo --> * Installer la dernière version stable de Rust en utilisant `rustup` * Mettre à jour Rust vers une nouvelle version * Ouvrir la documentation installée en local -* Écrire et exécuter un programme *Hello, world!* en utilisant directement +* Écrire et exécuter un programme “Hello, world!” en utilisant directement `rustc` * Créer et exécuter un nouveau projet en utilisant les conventions de Cargo diff --git a/FRENCH/src/ch02-00-guessing-game-tutorial.md b/FRENCH/src/ch02-00-guessing-game-tutorial.md index 21e04f5432..b2758a51b8 100644 --- a/FRENCH/src/ch02-00-guessing-game-tutorial.md +++ b/FRENCH/src/ch02-00-guessing-game-tutorial.md @@ -455,7 +455,7 @@ io::stdin().read_line(&mut supposition) ``` + @@ -884,7 +890,7 @@ l'en-tête de section `[dependencies]` que Cargo a créé pour vous : ```toml [dependencies] -rand = "0.3.14" +rand = "0.5.5" ``` Dans le fichier *Cargo.toml*, tout ce qui suit une en-tête fait partie de cette @@ -904,11 +910,11 @@ section, et ce jusqu'à ce qu'une autre section débute. La section `[dependencies]` permet d'indiquer à Cargo de quelles *crates* externes votre projet dépend, et de quelle version de ces *crates* vous avez besoin. Dans notre cas, on ajoute comme dépendance la crate `rand` avec la version -sémantique `0.3.14`. Cargo arrive à interpréter le +sémantique `0.5.5`. Cargo arrive à interpréter le [versionnage sémantique][semver] (aussi appelé *SemVer*), qui -est une convention d'écriture de numéros de version. En réalité, `0.3.14` est -une abréviation pour `^0.3.14`, ce qui signifie “toute version qui propose une -API publique compatible avec la version 0.3.14”. +est une convention d'écriture de numéros de version. En réalité, `0.5.5` est +une abréviation pour `^0.5.5`, ce qui signifie “toute version qui propose une +API publique compatible avec la version 0.5.5”. [semver]: http://semver.org @@ -922,13 +928,19 @@ du projet, comme dans l'encart 2-2 : ```text $ cargo build - Updating registry `https://github.com/rust-lang/crates.io-index` - Downloading rand v0.3.14 - Downloading libc v0.2.14 - Compiling libc v0.2.14 - Compiling rand v0.3.14 + Updating crates.io index + Downloaded rand v0.5.5 + Downloaded libc v0.2.62 + Downloaded rand_core v0.2.2 + Downloaded rand_core v0.3.1 + Downloaded rand_core v0.4.2 + Compiling rand_core v0.4.2 + Compiling libc v0.2.62 + Compiling rand_core v0.3.1 + Compiling rand_core v0.2.2 + Compiling rand v0.5.5 Compiling guessing_game v0.1.0 (file:///projects/guessing_game) - Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs + Finished dev [unoptimized + debuginfo] target(s) in 2.53 s ``` Une fois le registre mis à jour, Cargo lit la section `[dependencies]` et se charge de télécharger les *crates* que vous n'avez pas encore. Dans notre cas, bien que nous n'ayons spécifié qu'une seule dépendance, `rand`, Cargo a aussi -téléchargé la *crate* `libc`, car `rand` dépend de `libc` pour fonctionner. Une -fois le téléchargement terminé des *crates*, Rust les compile, puis compile -notre projet avec les dépendances disponibles. +téléchargé la *crate* `libc` et `rand_core`, car `rand` dépend d'elles pour +fonctionner. Une fois le téléchargement terminé des *crates*, Rust les compile, +puis compile notre projet avec les dépendances disponibles. @@ -1042,7 +1054,7 @@ Cargo embarque une fonctionnalité qui garantie que vous pouvez recompiler le même artéfact à chaque fois que vous ou quelqu'un d'autre compile votre code : Cargo va utiliser uniquement les versions de dépendances que vous avez utilisées jusqu'à ce que vous indiquiez le contraire. -Par exemple, que se passe-t-il si la semaine prochaine, la version 0.3.15 de la +Par exemple, que se passe-t-il si la semaine prochaine, la version 0.5.6 de la *crate* `rand` est publiée et qu'elle apporte une correction importante, mais aussi qu'elle produit une régression qui va casser votre code ? @@ -1055,7 +1067,7 @@ the *Cargo.lock* file. When you build your project in the future, Cargo will see that the *Cargo.lock* file exists and use the versions specified there rather than doing all the work of figuring out versions again. This lets you have a reproducible build automatically. In other words, your project will -remain at `0.3.14` until you explicitly upgrade, thanks to the *Cargo.lock* +remain at `0.5.5` until you explicitly upgrade, thanks to the *Cargo.lock* file. --> @@ -1068,7 +1080,7 @@ Quand vous recompilerez votre projet plus tard, Cargo verra que le fichier *Cargo.lock* existe et utilisera les versions précisées à l'intérieur au lieu de recommencer à déterminer toutes les versions demandées. Ceci vous permet d'avoir automatiquement des compilations reproductibles. -En d'autres termes, votre projet va rester sur la version `0.3.14` jusqu'à ce +En d'autres termes, votre projet va rester sur la version `0.5.5` jusqu'à ce que vous le mettiez à jour explicitement, grâce au fichier *Cargo.lock*. Mais par défaut, Cargo va rechercher uniquement les versions plus grandes que -`0.3.0` et inférieures à `0.4.0`. Si la *crate* `rand` a été publiée en deux -nouvelles versions, `0.3.15` et `0.4.0`, alors vous verrez ceci si vous +`0.5.5` et inférieures à `0.6.0`. Si la *crate* `rand` a été publiée en deux +nouvelles versions, `0.5.6` et `0.6.0`, alors vous verrez ceci si vous lancez `cargo update` : ```text $ cargo update - Updating registry `https://github.com/rust-lang/crates.io-index` - Updating rand v0.3.14 -> v0.3.15 + Updating crates.io index + Updating rand v0.5.5 -> v0.5.6 ``` À partir de ce moment, vous pouvez aussi constater un changement dans le fichier *Cargo.lock* indiquant que la version de la *crate* `rand` que vous utilisez -maintenant est la `0.3.15`. +maintenant est la `0.5.6`. @@ -1127,8 +1139,7 @@ ceci : ```toml [dependencies] - -rand = "0.4.0" +rand = "0.6.0" ``` + ```text $ cargo build @@ -1543,7 +1556,7 @@ error[E0308]: mismatched types -- > src/main.rs:23:21 | 23 | match supposition.cmp(&nombre_secret) { - | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable + | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integer | = note: expected type `&std::string::String` = note: found type `&{integer}` @@ -1758,7 +1771,8 @@ L'utilisation de `parse` peut facilement mener à une erreur. Si par exemple, le texte contient `A👍%`, il ne sera pas possible de le convertir en nombre. Comme elle peut échouer, la méthode `parse` retourne un type `Result`, comme celui que la méthode `read_line` retourne (comme nous l'avons vu plus tôt dans -[“Gérer les erreurs potentielles avec le type `Result`”](#gérer-les-erreurs-potentielles-avec-le-type-result)). +[“Gérer les erreurs potentielles avec le type +`Result`”](#gérer-les-erreurs-potentielles-avec-le-type-result)). Nous allons gérer ce `Result` de la même manière, avec à nouveau la méthode `expect`. Si `parse` retourne une variante `Err` de `Result` car elle ne peut pas créer un nombre à partir de la chaîne de caractères, l'appel à @@ -1911,11 +1925,12 @@ user can take advantage of that in order to quit, as shown here: L'utilisateur pourrait quand même interrompre le programme en utilisant le raccourci clavier ctrl-c. Mais il y a une autre façon d'échapper à ce monstre insatiable, comme nous -l'avons abordé dans la partie -[“Comparer le nombre saisi au nombre secret”](#comparer-le-nombre-saisi-au-nombre-secret) : -si l'utilisateur saisit quelque chose qui n'est pas un nombre, le programme va +l'avons abordé dans la partie [“Comparer le nombre saisi au nombre +secret”](#comparer-le-nombre-saisi-au-nombre-secret) : si +l'utilisateur saisit quelque chose qui n'est pas un nombre, le programme va planter. L'utilisateur peut procéder ainsi pour le quitter, comme ci-dessous : + + ```text $ cargo run diff --git a/FRENCH/src/ch03-00-common-programming-concepts.md b/FRENCH/src/ch03-00-common-programming-concepts.md new file mode 100644 index 0000000000..1bef574849 --- /dev/null +++ b/FRENCH/src/ch03-00-common-programming-concepts.md @@ -0,0 +1,53 @@ + + +# Les concepts courants de programmation + + + +Ce chapitre explique des concepts qui apparaissent dans presque tous les +langages de programmation, et la manière dont ils fonctionnent en Rust. De +nombreux langages sont basés sur des concepts communs. Les concepts présentés +dans ce chapitre ne sont pas spécifiques à Rust, mais nous les appliquerons +à Rust et nous expliquerons les conventions qui leur sont liées. + + + +Plus précisément, vous allez apprendre les concepts de variables, les types de +base, les fonctions, les commentaires, et les structures de contrôle. Ces +notions fondamentales seront présentes dans tous les programmes Rust, et les +apprendre dès le début vous procurera de solides bases pour débuter. + + + +> #### Mots-clés +> +> Le langage Rust possède un ensemble de *mots-clés* qui ont été réservés pour +> l'usage exclusif du langage, tout comme le font d'autres langages. Gardez à +> l'esprit que vous ne pouvez pas utiliser ces mots pour des noms de variables +> ou de fonctions. La plupart des mots-clés ont une signification spéciale, et +> vous les utiliserez pour réaliser de différentes tâches dans vos programmes +> Rust ; quelques-uns n'ont aucune fonctionnalité active pour le moment, mais +> ont été réservés pour être ajoutés plus tard à Rust. +> Vous pouvez trouver la liste de ces mots-clés dans l'annexe A. diff --git a/FRENCH/src/ch03-01-variables-and-mutability.md b/FRENCH/src/ch03-01-variables-and-mutability.md new file mode 100644 index 0000000000..517438d957 --- /dev/null +++ b/FRENCH/src/ch03-01-variables-and-mutability.md @@ -0,0 +1,570 @@ + + +## Les variables et la mutabilité + + + +Tel qu'abordé au chapitre 2, par défaut, les variables sont *immuables*. C'est +un des nombreux coups de pouce de Rust pour écrire votre code de façon à +garantir la sécurité et la concurrence sans problème. Cependant, vous avez quand +même la possibilité de rendre vos variables mutables *(modifiables)*. Explorons +comment et pourquoi Rust vous encourage à favoriser l'immuabilité, et pourquoi +parfois vous pourriez choisir d'y renoncer. + + + +Lorsqu'une variable est immuable, cela signifie qu'une fois qu'une valeur est +liée à un nom, vous ne pouvez pas changer cette valeur. À titre d'illustration, +générons un nouveau projet appelé *variables* dans votre dossier *projects* en +utilisant `cargo new variables`. + + + +Ensuite, dans votre nouveau dossier *variables*, ouvrez *src/main.rs* et +remplacez son code par le code suivant qui ne compile pas pour le moment : + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +fn main() { + let x = 5; + println!("La valeur de x est : {}", x); + x = 6; + println!("La valeur de x est : {}", x); +} +``` + + + +Sauvegardez et lancez le programme en utilisant `cargo run`. Vous devriez +avoir un message d'erreur comme celui-ci : + + + +```text +error[E0384]: cannot assign twice to immutable variable `x` + --> src/main.rs:4:5 + | +2 | let x = 5; + | - first assignment to `x` +3 | println!("La valeur de x est : {}", x); +4 | x = 6; + | ^^^^^ cannot assign twice to immutable variable +``` + + + +Cet exemple montre comment le compilateur vous aide à trouver les erreurs dans +vos programmes. Même si les erreurs de compilation peuvent s'avérer frustrantes, +elles signifient uniquement que, pour le moment, votre programme n'est pas en +train de faire ce que vous voulez qu'il fasse en toute sécurité ; elles ne +signifient *pas* que vous êtes un mauvais développeur ! Même les Rustacés +expérimentés continuent d'avoir des erreurs de compilation. + + + +Ce message d'erreur indique que la cause du problème est qu'il est *impossible +d'assigner à deux reprises la variable immuable `x`* (`cannot assign twice to +immutable variable x`). + + + +Il est important que nous obtenions des erreurs au moment de la compilation +lorsque nous essayons de changer une valeur qui a précédemment été déclarée +comme immuable, car cette situation particulière peut donner lieu à des bogues. +Si une partie de notre code part du principe qu'une valeur ne changera jamais et +qu'une autre partie de notre code modifie cette valeur, il est possible que la +première partie du code ne fasse pas ce pour quoi elle a été conçue. La cause de +ce genre de bogue peut être difficile à localiser après coup, en particulier +lorsque la seconde partie du code ne modifie que *parfois* cette valeur. + + + +Avec Rust, le compilateur garantit que lorsque nous déclarons qu'une variable ne +changera pas, elle ne changera vraiment pas. Cela signifie que lorsque vous +lisez et écrivez du code, vous n'avez pas à vous soucier d'où et comment la +valeur pourrait changer. Votre code est ainsi plus facile à comprendre. + + + +Mais la mutabilité peut s'avérer très utile. Les variables sont immuables par +défaut ; mais comme vous l'avez fait au chapitre 2, vous pouvez les rendre +mutables en ajoutant `mut` devant le nom de la variable. En plus de permettre à +cette valeur de changer, `mut` va signaler l'intention aux futurs lecteurs de ce +code que d'autres parties du code vont modifier la valeur de cette variable. + + + +Par exemple, modifions *src/main.rs* ainsi : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let mut x = 5; + println!("La valeur de x est : {}", x); + x = 6; + println!("La valeur de x est : {}", x); +} +``` + + + +Lorsque nous exécutons le programme, nous obtenons : + + + +```text +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) + Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs + Running `target/debug/variables` +La valeur de x est : 5 +La valeur de x est : 6 +``` + + + +En utilisant `mut`, nous avons permis à la valeur de `x` de passer de `5` à `6`. +Dans certains cas, on voudra rendre une variable mutable car cela +rendra le code plus pratique à écrire que s'il n'utilisait que des variables +immuables. + + + +Il y a d'autres compromis à envisager, en plus de la prévention des bogues. Par +exemple, dans le cas où vous utiliseriez des grosses structures de données, +muter une instance déjà existante peut être plus rapide que copier et retourner +une instance nouvellement allouée. Avec des structures de données plus petites, +créer de nouvelles instances avec un style de programmation fonctionnelle peut +rendre le code plus facile à comprendre, donc il peut valoir le coup de +sacrifier un peu de performance pour que le code gagne en clarté. + + + +### Différences entre les variables et les constantes + + + +Rendre impossible de changer la valeur d'une variable peut vous avoir rappelé un +autre concept de programmation que de nombreux autres langages possèdent : les +*constantes*. Comme les variables immuables, les constantes sont des valeurs qui +sont liées à un nom et qui ne peuvent être modifiées, mais il y a quelques +différences entre les constantes et les variables. + + + +D'abord, vous ne pouvez pas utiliser `mut` avec les constantes. Les constantes +ne sont pas seulement immuables par défaut − elles sont toujours immuables. + + + +On déclare les constantes en utilisant le mot-clé `const` à la place du +mot-clé `let`, et le type de la valeur *doit* être indiqué. Nous allons aborder +les types et les annotations de types dans la prochaine section, +[“Les types de données”][data-types], donc ne vous souciez pas +des détails pour le moment. Sachez seulement que vous devez toujours indiquer le +type. + + + +Les constantes peuvent être déclarées à n'importe quel endroit du code, y +compris la portée globale, ce qui les rend très utiles pour des valeurs que de +nombreuses parties de votre code ont besoin de connaître. + + + +La dernière différence est que les constantes ne peuvent être définies que par +une expression constante, et non pas le résultat d'un appel de fonction ou toute +autre valeur qui ne pourrait être calculée qu'à l'exécution. + + + +Voici un exemple d'une déclaration de constante où le nom de la constante est +`MAX_POINTS` et où sa valeur est définie à 100 000. (En Rust, la convention de +nommage des constantes est de les écrire tout en majuscule avec des tirets bas +entre les mots, et des tirets bas peuvent être ajoutés entre les nombres pour +améliorer la lisibilité) : + +```rust +const MAX_POINTS: u32 = 100_000; +``` + + + +Les constantes sont valables pendant toute la durée d'exécution du programme +au sein de la portée dans laquelle elles sont déclarées, ce qui en fait de +très bons choix lorsque plusieurs parties du programme doivent connaître +certaines valeurs, comme par exemple le nombre maximum de points qu'un joueur +est autorisé à gagner ou encore la vitesse de la lumière. + + + +Déclarer des valeurs codées en dur et utilisées tout le long de votre programme +en tant que constantes est utile pour faire comprendre la signification de ces +valeurs dans votre code aux futurs développeurs. Cela permet également de +n'avoir qu'un seul endroit de votre code à modifier si cette valeur codée en dur +doit être mise à jour à l'avenir. + + + +### Le masquage + + + +Comme nous l'avons vu dans la section [“Comparer le nombre saisi au nombre +secret”][comparing-the-guess-to-the-secret-number] +du jeu de devinettes au chapitre 2, on peut déclarer une nouvelle variable +avec le même nom qu'une variable précédente, et la nouvelle variable +masquera la première. Les Rustacés disent que la première variable est *masquée* +par la seconde, ce qui signifie que la valeur de la seconde variable sera ce que +nous obtiendrons lorsque nous utiliserons cette variable. Nous pouvons créer un +masque d'une variable en utilisant le même nom de variable et en réutilisant le +mot-clé `let` comme ci-dessous : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let x = 5; + + let x = x + 1; + + let x = x * 2; + + println!("La valeur de x est : {}", x); +} +``` + + + +Au début, ce programme lie `x` à la valeur `5`. Puis il crée un masque de `x` +en répétant `let x =`, en récupérant la valeur d'origine et lui ajoutant `1` : +la valeur de `x` est désormais `6`. La troisième instruction `let` crée un autre +masque de `x`, en récupérant la précédente valeur et en la multipliant par `2` +pour donner à `x` la valeur finale de `12`. Lorsque nous exécutons ce programme, +nous obtenons ceci : + + + +```text +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) + Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs + Running `target/debug/variables` +La valeur de x est : 12 +``` + + + +Créer un masque est différent que de marquer une variable comme étant `mut`, +car à moins d'utiliser une nouvelle fois le mot-clé `let`, nous obtiendrons une +erreur de compilation si nous essayons de réassigner cette variable par +accident. Nous pouvons effectuer quelques transformations sur une valeur en +utilisant `let`, mais faire en sorte que la variable soit immuable après que ces +transformations ont été appliquées. + + + +Comme nous créons une nouvelle variable lorsque nous utilisons le mot-clé `let` +une nouvelle fois, l'autre différence entre le `mut` et la création d'un masque +est que cela nous permet de changer le type de la valeur, mais en réutilisant +le même nom. Par exemple, imaginons un programme qui demande à l'utilisateur +le nombre d'espaces qu'il souhaite entre deux portions de texte en saisissant +des espaces, mais que nous voulons plutôt stocker cela sous forme de nombre : + + + +```rust +let espaces = " "; +let espaces = espaces.len(); +``` + + + +Cette solution est autorisée car la première variable `espaces` est du type +chaîne de caractères *(string)*, alors que la seconde variable `espaces`, qui +est une toute nouvelle variable qui se trouve avoir le même nom que la première, +est du type nombre. L'utilisation du masquage nous évite ainsi d'avoir à trouver +des noms différents, comme `espaces_str` et `espaces_num` ; nous pouvons plutôt +simplement réutiliser le nom `espaces`. Cependant, si nous essayons d'utiliser +`mut` pour faire ceci, comme ci-dessous, nous avons une erreur de compilation : + + + +```rust,ignore +let mut espaces = " "; +espaces = espaces.len(); +``` + + + +L'erreur indique que nous ne pouvons pas muter le type d'une variable : + + + +```text +error[E0308]: mismatched types + -- > src/main.rs:3:14 + | +3 | espaces = espaces.len(); + | ^^^^^^^^^^^^^ expected &str, found usize + | + = note: expected type `&str` + found type `usize` +``` + + + +Maintenant que nous avons découvert comment fonctionnent les variables, étudions +les types de données qu'elles peuvent prendre. + + + +[comparing-the-guess-to-the-secret-number]: +ch02-00-guessing-game-tutorial.html#comparer-le-nombre-saisi-au-nombre-secret +[data-types]: ch03-02-data-types.html#les-types-de-données diff --git a/FRENCH/src/ch03-02-data-types.md b/FRENCH/src/ch03-02-data-types.md new file mode 100644 index 0000000000..4729b05608 --- /dev/null +++ b/FRENCH/src/ch03-02-data-types.md @@ -0,0 +1,994 @@ + + +## Les types de données + + + +Chaque valeur en Rust est d'un *type* bien déterminé, qui indique à Rust quel +genre de données il manipule pour qu'il sache comment traiter ces données. +Nous allons nous intéresser à deux catégories de types de données : les +scalaires et les composés. + + + +Gardez à l'esprit que Rust est langage *statiquement typé*, ce qui signifie +qu'il doit connaître les types de toutes les variables au moment de la +compilation. Le compilateur peut souvent déduire quel type utiliser en se basant +sur la valeur et sur la façon dont elle est utilisée. Dans les cas où plusieurs +types sont envisageables, comme lorsque nous avons converti une chaîne de +caractères en un type numérique en utilisant `parse` dans la section +[“Comparer le nombre saisi au nombre +secret”][comparing-the-guess-to-the-secret-number] +du chapitre 2, nous devons ajouter une annotation de type, comme ceci : + + + +```rust +let supposition: u32 = "42".parse().expect("Ce n'est pas un nombre !"); +``` + + + +Si nous n'ajoutons pas l'annotation de type ici, Rust affichera l'erreur +suivante, signifiant que le compilateur a besoin de plus d'informations pour +déterminer quel type nous souhaitons utiliser : + + + +```text +error[E0282]: type annotations needed + --> src/main.rs:2:9 + | +2 | let supposition = "42".parse().expect("Ce n'est pas un nombre !"); + | ^^^^^^^^^^^ + | | + | cannot infer type for `_` + | consider giving `supposition` a type +``` + + + +Vous découvrirez différentes annotations de type au fur et à mesure que nous +aborderons les autres types de données. + + + +### Types scalaires + + + +Un type *scalaire* représente une seule valeur. Rust possède quatre types +principaux de scalaires : les entiers, les nombres à virgule flottante, les +booléens et les caractères. Vous les connaissez sûrement d'autres langages de +programmation. Regardons comment ils fonctionnent avec Rust. + + + +#### Types de nombres entiers + + + +Un *entier* est un nombre sans partie décimale. Nous avons utilisé un entier +précédemment dans le chapitre 2, le type `u32`. Cette déclaration de type +indique que la valeur à laquelle elle est associée doit être un entier non signé +encodé sur 32 bits dans la mémoire (les entiers pouvant prendre des valeurs +négatives commencent par un `i` (comme *integer* : “entier”), plutôt que par un +`u` comme *unsigned* : “non signé”). Le tableau 3-1 montre les types +d'entiers intégrés au langage. Chaque variante dans les colonnes “Signé” et +“Non signé” (par exemple `i16`) peut être utilisée pour déclarer le type d'une +valeur entière. + + + +Tableau 3-1 : les types d'entiers en Rust + + + +| Taille | Signé | Non signé | +|---------|---------|-----------| +| 8 bits | `i8` | `u8` | +| 16 bits | `i16` | `u16` | +| 32 bits | `i32` | `u32` | +| 64 bits | `i64` | `u64` | +| 128 bits| `i128` | `u128` | +| archi | `isize` | `usize` | + + + + + +Chaque variante peut-être signée ou non signée et possède une taille explicite. +*Signé* et *non signé* veut dire respectivement que le nombre peut prendre ou +non des valeurs négatives ou positives — en d'autres termes, si l'on peut lui +attribuer un signe (signé) ou s'il sera toujours positif et que l'on peut donc +le représenter sans signe (non signé). C'est comme écrire des nombres sur du +papier : quand le signe est important, le nombre est écrit avec un signe plus +ou un signe moins ; en revanche, quand le nombre est forcément positif, on peut +l'écrire sans son signe. Les nombres signés sont stockés en utilisant le +[complément à deux](https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_deux). + + + +Chaque variante signée peut stocker des nombres allant de −(2*n* − 1) +à 2*n* − 1 − 1 inclus, où *n* est le nombre de bits que cette +variante utilise. +Un `i8` peut donc stocker des nombres allant de −(27) à +27 − 1, c'est-à-dire de −128 à 127. Les variantes non signées peuvent +stocker des nombres de 0 à 2*n* − 1, donc un `u8` peut stocker +des nombres allant de 0 à 28 − 1, c'est-à-dire de 0 à 255. + + + +De plus, les types `isize` et `usize` dépendent du type d'ordinateur sur lequel +votre programme va s'exécuter : 64 bits si vous utilisez une architecture +64 bits ou 32 bits si vous utilisez une architecture 32 bits. + + + +Vous pouvez écrire des littéraux d'entiers dans chacune des formes décrites dans +le tableau 3-2. Notez que chaque littéral numérique excepté l'octet accepte un +suffixe de type, comme `57u8`, et `_` comme séparateur visuel, comme par exemple +`1_000`. + + + +Tableau 3-2 : Les littéraux d'entiers en Rust + + + +| Littéral numérique | Exemple | +|------------------------|---------------| +| Décimal | `98_222` | +| Hexadécimal | `0xff` | +| Octal | `0o77` | +| Binaire | `0b1111_0000` | +| Octet (`u8` seulement) | `b'A'` | + + + +Comment pouvez-vous déterminer le type d'entier à utiliser ? Si vous n'êtes pas +sûr, les choix par défaut de Rust sont généralement de bons choix, et le type +d'entier par défaut est le `i32` : c'est souvent le plus rapide, même sur les +systèmes 64 bits. La principale utilisation d'un `isize` ou d'un `usize` est +lorsque l'on indexe une quelconque collection. + + + +> ##### Dépassement d'entier +> +> Imaginons que vous avez une variable de type `u8` qui peut stocker des +> valeurs entre 0 et 255. Si vous essayez de changer la variable pour une valeur +> en dehors de cet intervalle, comme 256, vous aurez un dépassement +> d'entier *(integer overflow)*. Rust possède quelques règles intéressantes +> concernant ce comportement. Quand vous compilez en mode débogage, Rust +> embarque des vérifications pour détecter les cas de dépassements d'entiers qui +> pourraient faire *paniquer* votre programme à l'exécution si ce phénomène se +> produit. Rust utilise le terme *paniquer* quand un programme se termine +> avec une erreur ; nous verrons plus en détail les *paniques* dans une section +> du [chapitre 9](ch09-01-unrecoverable-errors-with-panic.html). +> +> Lorsque vous compilez en mode publication *(release)* avec le drapeau +> `--release`, Rust ne va *pas* vérifier les potentiels dépassements d'entiers +> qui peuvent faire paniquer le programme. En revanche, en cas de dépassement, +> Rust va effectuer un *rebouclage du complément à deux*. Pour faire simple, les +> valeurs supérieures à la valeur maximale du type seront “rebouclées” depuis la +> valeur minimale que le type peut stocker. Dans cas d'un `u8`, 256 devient 0, +> 257 devient 1, et ainsi de suite. Le programme ne va paniquer, mais +> la variable va avoir une valeur qui n'est probablement pas ce que vous +> attendez à avoir. Se fier au comportement du rebouclage lors du +> dépassement d'entier est considéré comme une faute. Si vous voulez reboucler +> explicitement, vous pouvez utiliser le type [`Wrapping`][wrapping] de la +> bibliothèque standard. + + + +#### Types de nombres à virgule flottante + + + +Rust possède également deux types primitifs pour les *nombres à virgule +flottante* (ou *flottants*), qui sont des nombres avec des décimales. Les types +de flottants en Rust sont les `f32` et les `f64`, qui ont respectivement une +taille en mémoire de 32 bits et 64 bits. Le type par défaut est le `f64` car sur +les processeurs récents ce type est quasiment aussi rapide qu'un `f32` mais est +plus précis. + + + +Voici un exemple montrant l'utilisation de nombres à virgule flottante : + + + +Nom du ficher: src/main.rs + +```rust +fn main() { + let x = 2.0; // f64 + + let y: f32 = 3.0; // f32 +} +``` + + + +Les nombres à virgule flottante sont représentés selon la norme IEEE-754. Le +type `f32` est un flottant à simple précision, et le `f64` est à double +précision. + + + +#### Les opérations numériques + + + +Rust offre les opérations mathématiques de base dont vous auriez besoin pour +tous les types de nombres : addition, soustraction, multiplication, division et +modulo. Le code suivant montre comment utiliser chacune d'elles avec une +instruction `let` : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + // addition + let somme = 5 + 10; + + // soustraction + let difference = 95.5 - 4.3; + + // multiplication + let produit = 4 * 30; + + // division + let quotient = 56.7 / 32.2; + + // modulo + let reste = 43 % 5; +} +``` + + + +Chaque expression de ces instructions utilise un opérateur mathématique et +calcule une valeur unique, qui est ensuite attribuée à une variable. L'annexe B +présente une liste de tous les opérateurs que Rust fournit. + + + +#### Le type booléen + + + +Comme dans la plupart des langages de programmation, un type booléen a deux +valeurs possibles en Rust : `true` (vrai) et `false` (faux). Les booléens +prennent un octet en mémoire. Le type booléen est désigné en utilisant `bool`. +Par exemple : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let t = true; + + let f: bool = false; // avec une annotation de type explicite +} +``` + + + +Les valeurs booléennes sont principalement utilisées par les structures +conditionnelles, comme l'expression `if`. Nous aborderons le fonctionnement +de `if` en Rust dans la section +[“Les structures de contrôle”][control-flow]. + + + +#### Le type caractère + + + +Jusqu'à présent, nous avons utilisé uniquement des nombres, mais Rust peut aussi +travailler avec des lettres. Le type `char` (comme *character*) est le type de +caractère le plus rudimentaire, et le code suivant va vous montrer une façon de +l'utiliser. (A noter que les `char` sont écrits avec des guillemets simples, +contrairement aux chaînes, qui utilisent des guillemets doubles.) + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let c = 'z'; + let z = 'ℤ'; + let chat_aux_yeux_de_coeur = '😻'; +} +``` + + + +Le type `char` de Rust prend quatre octets en mémoire et représente une valeur +scalaire Unicode, ce qui veut dire que cela représente plus de caractères que +l'ASCII. Les lettres accentuées ; les caractères chinois, japonais et coréens ; +les emoji ; les espaces de largeur nulle ont tous une valeur pour `char` avec +Rust. Les valeurs scalaires Unicode vont de `U+0000` à `U+D7FF` et de `U+E000` à +`U+10FFFF` inclus. Cependant, le concept de “caractère” n'est pas clairement +défini par Unicode, donc votre notion de “caractère” peut ne pas correspondre +à ce qu'est un `char` en Rust. Nous aborderons ce sujet plus en détail au +[chapitre 8](ch08-02-strings.html). + + + +### Les types composés + + + +Les *types composés* peuvent regrouper plusieurs valeurs dans un seul type. Rust +a deux types composés de base : les *tuples* et les tableaux *(arrays)*. + + + +#### Le type *tuple* + + + +Un *tuple* est une manière générale de regrouper plusieurs valeurs +de types différents en un seul type composé. Les tuples ont une taille fixée : +à partir du moment où ils ont été déclarés, on ne peut pas y ajouter ou enlever +des valeurs. + + + +Nous créons un *tuple* en écrivant une liste séparée par des virgules entre des +parenthèses. Chaque emplacement dans le tuple a un type, et les types de chacune +des valeurs dans le tuple n'ont pas forcément besoin d'être les mêmes. +Nous avons ajouté des annotations de type dans cet exemple, mais c'est +optionnel : + + + +Fichier : src/main.rs + +```rust +fn main() { + let tup: (i32, f64, u8) = (500, 6.4, 1); +} +``` + + + +La variable `tup` est liée à tout le tuple, car un tuple est considéré +comme étant un unique élément composé. Pour obtenir un élément précis de ce +tuple, nous pouvons utiliser un filtrage par motif *(pattern matching)* pour +déstructurer ce tuple, comme ceci : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let tup = (500, 6.4, 1); + + let (x, y, z) = tup; + + println!("La valeur de y est : {}", y); +} +``` + + + +Le programme commence par créer un tuple et il l'assigne à la variable `tup`. +Il utilise ensuite un motif avec `let` pour prendre `tup` et le scinder en +trois variables distinctes : `x`, `y`, et `z`. +On appelle cela *déstructurer*, car il divise le tuple en trois parties. +Puis finalement, le programme affiche la valeur de `y`, qui est `6.4`. + + + +En plus de pouvoir déstructurer avec un filtrage par motif, nous pouvons accéder +directement à chaque élément du tuple en utilisant un point (`.`) suivi de +l'indice de la valeur que nous souhaitons obtenir. Par exemple : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let x: (i32, f64, u8) = (500, 6.4, 1); + + let cinq_cents = x.0; + + let six_virgule_quatre = x.1; + + let un = x.2; +} +``` + + + +Ce programme crée un tuple, `x`, puis crée une nouvelle variable pour +chaque élément en utilisant leur indices respectifs. Comme dans de nombreux +langages de programmation, le premier indice d'un tuple est 0. + + + +#### Le type tableau + + + +Un autre moyen d'avoir une collection de plusieurs valeurs est d'utiliser +un *tableau*. Contrairement aux tuples, chaque élément d'un tableau doit être du +même type. Les tableaux de Rust diffèrent de ceux de certains autres langages +car les tableaux de Rust ont une taille fixe, comme les tuples. + + + +Avec Rust, les valeurs stockées dans un tableau sont écrites dans une +liste séparée par des virgules entre des crochets : + + + +Fichier : src/main.rs + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; +} +``` + + + +Les tableaux sont utiles quand vous voulez que vos données soient allouées sur +la pile *(stack)* plutôt que sur le tas *(heap)* (nous expliquerons la pile et +le tas au chapitre 4) ou lorsque vous voulez vous assurer que vous avez toujours +un nombre fixe d'éléments. Cependant, un tableau n'est pas aussi flexible qu'un +vecteur *(vector)*. Un vecteur est un type de collection de données +similaire qui est fourni par la bibliothèque standard qui, lui, peut +grandir ou rétrécir en taille. Si vous ne savez pas si vous devez utiliser un +tableau ou un vecteur, vous devriez probablement utiliser un vecteur. Le +chapitre 8 expliquera les vecteurs. + + + +Un exemple de cas où vous pourriez avoir recours à un tableau plutôt qu'à un +vecteur est un programme qui nécessite de connaître les noms des mois de +l'année. +Il est très improbable qu'un tel programme ait besoin d'ajouter ou de supprimer +des mois, donc vous pouvez utiliser un tableau car vous savez qu'il contiendra +toujours 12 éléments : + + + +```rust +let mois = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", + "Août", "Septembre", "Octobre", "Novembre", "Décembre"]; +``` + + + +Vous pouvez écrire le type d'un tableau en utilisant des crochets, et entre ces +crochets y ajouter le type de chaque élément, un point-virgule, et ensuite le +nombre d'éléments dans le tableau, comme ceci : + +```rust +let a: [i32; 5] = [1, 2, 3, 4, 5]; +``` + + + +Ici, `i32` est le type de chaque élément. Après le point-virgule, le nombre `5` +indique que le tableau contient cinq éléments. + + + +L'écriture d'un type de tableau de cette manière ressemble à une autre syntaxe +pour initialiser un tableau : si vous voulez créer un tableau qui contient la +même valeur pour chaque élément, vous pouvez préciser la valeur initiale, suivie +par un point-virgule, et ensuite la taille du tableau, le tout entre crochets, +comme ci-dessous : + +```rust +let a = [3; 5]; +``` + + + +Le tableau `a` va contenir `5` éléments qui auront tous la valeur +initiale `3`. C'est la même chose que d'écrire `let a = [3, 3, 3, 3, 3];` mais +de manière plus concise. + + + +##### Accéder aux éléments d'un tableau + + + +Un tableau est un simple bloc de mémoire alloué sur la pile. Vous pouvez accéder +aux éléments d'un tableau en utilisant l'indexation, comme ceci : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let premier = a[0]; + let second = a[1]; +} +``` + + + +Dans cet exemple, la variable qui s'appelle `premier` aura la valeur `1`, car +c'est la valeur à l'indice `[0]` dans le tableau. La variable `second` +récupèrera la valeur `2` depuis l'indice `[1]` du tableau. + + + +##### Accès incorrect à un élément d'un tableau + + + +Que se passe-t-il quand vous essayez d'accéder à un élément d'un tableau qui se +trouve après la fin du tableau ? Imaginons que vous changiez l'exemple par le +code suivant, qui va compiler mais qui va quitter avec une erreur quand il sera +exécuté : + + + +Fichier : src/main.rs + + + +```rust,ignore,panics +fn main() { + let a = [1, 2, 3, 4, 5]; + let indice = 10; + + let element = a[indice]; + + println!("La valeur de l'élément est : {}", element); +} +``` + + + +Exécuter ce code en utilisant `cargo run` va donner le résultat suivant : + +```text +$ cargo run + Compiling arrays v0.1.0 (file:///projects/arrays) + Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs + Running `target/debug/arrays` +thread 'main' panicked at 'index out of bounds: the len is 5 but the index is + 10', src/main.rs:5:19 +note: Run with `RUST_BACKTRACE=1` for a backtrace. +``` + + + +La compilation n'a pas produit d'erreur, mais le programme a rencontré une +erreur *à l'exécution* et ne s'est pas terminé avec succès. Quand vous essayez +d'accéder à un élément en utilisant l'indexation, Rust va vérifier que l'indice +que vous avez demandé est plus petit que la taille du tableau. Si l'indice est +supérieur ou égal à la taille du tableau, Rust va *paniquer*. + + + +C'est un premier exemple pratique des principes de sécurité de Rust. Dans de +nombreux langages de bas niveau, ce genre de vérification n'est pas effectuée, +et quand vous utilisez un indice incorrect, de la mémoire invalide peut être +récupérée. Rust vous protège de ce genre d'erreur en quittant immédiatement +l'exécution au lieu de permettre l'accès en mémoire et +continuer son déroulement. Le chapitre 9 expliquera la gestion d'erreurs de +Rust. + + + +[comparing-the-guess-to-the-secret-number]: +ch02-00-guessing-game-tutorial.html#comparer-le-nombre-saisi-au-nombre-secret +[control-flow]: ch03-05-control-flow.html#les-structures-de-contrôle +[wrapping]: https://doc.rust-lang.org/std/num/struct.Wrapping.html diff --git a/FRENCH/src/ch03-03-how-functions-work.md b/FRENCH/src/ch03-03-how-functions-work.md new file mode 100644 index 0000000000..f01ddc9883 --- /dev/null +++ b/FRENCH/src/ch03-03-how-functions-work.md @@ -0,0 +1,787 @@ + + +## Les fonctions + + + +Les fonctions sont omniprésentes dans le code Rust. Vous avez déjà vu l'une des +fonctions les plus importantes du langage : la fonction `main`, qui est le point +d'entrée de beaucoup de programmes. Vous avez aussi vu le mot-clé `fn`, qui vous +permet de déclarer des nouvelles fonctions. + + + +Le code Rust utilise le *snake case* comme convention de style de nom des +fonctions et des variables. Avec le *snake case*, toutes les lettres sont en +minuscule et on utilise des tirets bas pour séparer les mots. Voici un programme +qui est un exemple de définition de fonction : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + println!("Hello, world!"); + + une_autre_fonction(); +} + +fn une_autre_fonction() { + println!("Une autre fonction."); +} +``` + + + +La définition d'une fonction avec Rust commence par `fn` et a une paire de +parenthèses après le nom de la fonction. Les accolades indiquent au compilateur +où le corps de la fonction commence et où il se termine. + + + +Nous pouvons appeler n'importe quelle fonction que nous avons définie en +utilisant son nom, suivi d'une paire de parenthèses. Comme `une_autre_fonction` +est définie dans le programme, elle peut être appelée à l'intérieur de la +fonction `main`. Remarquez que nous avons défini `une_autre_fonction` *après* +la fonction `main` dans le code source ; nous aurions aussi pu la définir avant. +Rust ne se soucie pas de l'endroit où vous définissez vos fonctions, du moment +qu'elles sont bien définies quelque part. + + + +Créons un nouveau projet de binaire qui s'appellera *functions* afin d'en +apprendre plus sur les fonctions. Ajoutez l'exemple `une_autre_fonction` dans le +*src/main.rs* et exécutez-le. Vous devriez avoir ceci : + + + +```text +$ cargo run + Compiling functions v0.1.0 (file:///projects/functions) + Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs + Running `target/debug/functions` +Hello, world! +Une autre fonction. +``` + + + +Les lignes s'exécutent dans l'ordre dans lequel elles apparaissent dans la +fonction `main`. D'abord, le message `Hello, world!` est écrit, et ensuite +`une_autre_fonction` est appelée et son message est affiché. + + + +### Les paramètres de fonctions + + + +Les fonctions peuvent aussi être définies avec des *paramètres*, qui sont des +variables spéciales qui font partie de la signature de la fonction. Quand une +fonction a des paramètres, vous pouvez lui fournir des valeurs concrètes avec +ces paramètres. Techniquement, ces valeurs concrètes sont appelées des +*arguments*, mais dans une conversation courante, on a tendance à +confondre les termes *paramètres* et *arguments* pour désigner soit les +variables dans la définition d'une fonction, soit les valeurs concrètes passées +quand on appelle une fonction. + + + +La version réécrite de `une_autre_fonction` montre comment utiliser un paramètre +avec Rust : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + une_autre_fonction(5); +} + +fn une_autre_fonction(x: i32) { + println!("La valeur de x est : {}", x); +} +``` + + + +En exécutant ce programme, vous devriez obtenir ceci : + + + +```text +$ cargo run + Compiling functions v0.1.0 (file:///projects/functions) + Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs + Running `target/debug/functions` +La valeur de x est : 5 +``` + + + +La déclaration de `une_autre_fonction` a un paramètre nommé `x`. Le type de +`x` a été déclaré comme `i32`. Quand `5` est passé à `une_autre_fonction`, la +macro `println!` place `5` là où la paire d'accolades `{}` a été placée dans la +chaîne de formatage. + + + +Dans la signature d'une fonction, vous *devez* déclarer le type de chaque +paramètre. C'est un choix délibéré de conception de Rust : exiger l'annotation +de type dans la définition d'une fonction fait en sorte que le compilateur n'a +presque plus besoin que vous les utilisiez autre part pour qu'il comprenne ce +que vous voulez faire. + + + +Lorsque vous souhaitez qu'une fonction ait plusieurs paramètres, séparez les +paramètres avec des virgules, comme ceci : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + une_autre_fonction(5, 6); +} + +fn une_autre_fonction(x: i32, y: i32) { + println!("La valeur de x est : {}", x); + println!("La valeur de y est : {}", y); +} +``` + + + +Cet exemple crée une fonction avec deux paramètres, chacun d'eux sont du type +`i32`. La fonction affiche ensuite les valeurs de ses deux paramètres. +Notez que les paramètres des fonctions n'ont pas besoin d'être du même type, +nous sommes dans cette situation uniquement pour les besoins de notre exemple. + + + +Essayons d'exécuter ce code. Remplacez le programme présent actuellement dans +votre fichier *src/main.rs* de votre projet *functions* par l'exemple précédent +et lancez-le en utilisant `cargo run` : + + + +```text +$ cargo run + Compiling functions v0.1.0 (file:///projects/functions) + Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs + Running `target/debug/functions` +La valeur de x est : 5 +La valeur de y est : 6 +``` + + + +Comme nous avons appelé la fonction avec la valeur `5` pour `x` et `6` pour +`y`, deux lignes sont affichées avec ces valeurs. + + + +### Corps de fonction avec des instructions et des expressions + + + +Les corps de fonctions sont constitués d'une série d'instructions qui se +termine éventuellement par une expression. Jusqu'à présent, nous avons vu des +fonctions sans expression à la fin, mais vous avez déjà vu une expression faire +partie d'une instruction. Comme Rust est un langage basé sur des expressions, +il est important de faire la distinction. D'autres langages ne font pas de +telles distinctions, donc penchons-nous sur ce que sont les instructions et les +expressions et comment leurs différences influent sur le corps des fonctions. + + + +Nous avons déjà utilisé des instructions et des expressions. Les *instructions* +effectuent des actions et ne retournent aucune valeur. +Les *expressions* sont évaluées pour retourner une valeur comme résultat. +Voyons quelques exemples. + + + +Créer une variable en lui assignant une valeur avec le mot-clé `let` constitue +une instruction. Dans l'encart 3-1, `let y = 6;` est une instruction. + + + +Fichier : src/main.rs + +```rust +fn main() { + let y = 6; +} +``` + + + + + +Encart 3-1 : une fonction `main` qui contient une +instruction + + + +La définition d'une fonction est aussi une instruction ; l'intégralité de +l'exemple précédent est une instruction à elle toute seule. + + + +Une instruction ne retourne pas de valeur. Ainsi, vous ne pouvez pas assigner +le résultat d'une instruction `let` à une autre variable, comme le code suivant +essaye de le faire, car vous obtiendrez une erreur : + + + +Fichier : src/main.rs + +```rust,ignore,does_not_compile +fn main() { + let x = (let y = 6); +} +``` + + + +Quand vous exécutez ce programme, l'erreur que vous obtenez devrait ressembler à +ceci : + +```text +$ cargo run + Compiling functions v0.1.0 (file:///projects/functions) +error: expected expression, found statement (`let`) + -- > src/main.rs:2:14 + | +2 | let x = (let y = 6); + | ^^^ + | + = note: variable declaration using `let` is a statement +``` + + + +L'instruction `let y = 6` ne retourne pas de valeur, donc cela ne peut pas +devenir une valeur de `x`. Ceci est différent d'autres langages, comme le C ou +Ruby, où l'assignation retourne la valeur de l'assignation. Dans ces +langages, vous pouvez écrire `x = y = 6` et avoir ainsi `x` et `y` qui ont +chacun la valeur `6` ; cela n'est pas possible avec Rust. + + + +Les expressions sont évaluées et seront ce que vous écrirez le plus en Rust +(hormis les instructions). Prenez une simple opération mathématique, comme +`5 + 6`, qui est une expression qui s'évalue à la valeur `11`. Les expressions +peuvent faire partie d'une instruction : dans l'encart 3-1, le `6` dans +l'instruction `let y = 6;` est une expression qui s'évalue à la valeur `6`. +L'appel de fonction est aussi une expression. L'appel de macro est une +expression. Le bloc que nous utilisons pour créer une nouvelle portée, `{}`, +est une expression, par exemple : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let x = 5; + + let y = { + let x = 3; + x + 1 + }; + + println!("La valeur de y est : {}", y); +} +``` + + + +L'expression suivante… + +```rust,ignore +{ + let x = 3; + x + 1 +} +``` + + + +… est un bloc qui, dans ce cas, s'évalue à `4`. Cette valeur est assignée à `y` +dans le cadre de l'instruction `let`. Remarquez la ligne `x + 1` qui ne se +termine pas par un point-virgule, ce qui est différent de la plupart +des lignes que vous avez vues jusque là. Les expressions n'ont pas de +point-virgule de fin de ligne. Si vous ajoutez un point-virgule à la fin de +l'expression, vous la transformez en instruction, qui ne va donc pas retourner +de valeur. Gardez ceci à l'esprit quand nous aborderons prochainement les +valeurs de retour des fonctions ainsi que les expressions. + + + +### Les fonctions qui retournent des valeurs + + + +Les fonctions peuvent retourner des valeurs au code qui les appelle. +Nous ne nommons pas les valeurs de retour, mais nous devons déclarer +leur type après une flèche (`->`). En Rust, la valeur de retour de la fonction +est la même que la valeur de l'expression finale dans le corps de la fonction. +Vous pouvez sortir prématurément d'une fonction en utilisant le mot-clé `return` +et en précisant la valeur de retour, mais la plupart des fonctions vont +retourner implicitement la dernière expression. +Voici un exemple d'une fonction qui retourne une valeur : + + + +Fichier : src/main.rs + + + +```rust +fn cinq() -> i32 { + 5 +} + +fn main() { + let x = cinq(); + + println!("La valeur de x est : {}", x); +} +``` + + + +Il n'y a pas d'appel de fonction, de macro, ni même d'instruction `let` dans la +fonction `cinq` — uniquement le nombre `5` tout seul. C'est une fonction +parfaitement valide avec Rust. Remarquez que le type de retour de la fonction a +été précisé aussi, avec `-> i32`. Essayez d'exécuter ce code ; le résultat +devrait ressembler à ceci : + + + +```text +$ cargo run + Compiling functions v0.1.0 (file:///projects/functions) + Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs + Running `target/debug/functions` +La valeur de x est : 5 +``` + + + +Le `5` dans `cinq` est la valeur de retour de la fonction, ce qui explique le +type de retour de `i32`. Regardons cela plus en détail. Il y a deux éléments +importants : premièrement, la ligne `let x = cinq();` dit que nous utilisons +la valeur de retour de la fonction pour initialiser la variable. Comme la +fonction `cinq` retourne un `5`, cette ligne revient à faire ceci : + +```rust +let x = 5; +``` + + + +Deuxièmement, la fonction `cinq` n'a pas de paramètre et déclare le type de +valeur de retour, mais le corps de la fonction est un simple `5` sans +point-virgule car c'est une expression dont nous voulons retourner la valeur. + + + +Regardons un autre exemple : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let x = plus_un(5); + + println!("La valeur de x est : {}", x); +} + +fn plus_un(x: i32) -> i32 { + x + 1 +} +``` + + + +Exécuter ce code va afficher `La valeur de x est : 6`. Mais si nous ajoutons un +point-virgule à la fin de la ligne qui contient `x + 1`, ce qui la transforme +d'une expression à une instruction, nous obtenons une erreur. + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +fn main() { + let x = plus_un(5); + + println!("La valeur de x est : {}", x); +} + +fn plus_un(x: i32) -> i32 { + x + 1; +} +``` + + + +Compiler ce code va produire une erreur, comme ci-dessous : + + + +```text +error[E0308]: mismatched types + -- > src/main.rs:7:28 + | +7 | fn plus_un(x: i32) -> i32 { + | ___________________________^ +8 | | x + 1; + | | - help: consider removing this semicolon +9 | | } + | |_^ expected i32, found () + | + = note: expected type `i32` + found type `()` +``` + + + +Le message d'erreur principal, “mismatched types” *(types inadéquats)* donne le +cœur du problème de ce code. La définition de la fonction `plus_un` dit qu'elle +va retourner un `i32`, mais les instructions ne retournent pas de valeur, ceci +est donc représenté par `()`, un *tuple* vide. Par conséquent, rien n'est +retourné, ce qui contredit la définition de la fonction et provoque une erreur. +Rust affiche un message qui peut aider à corriger ce problème : il suggère +d'enlever le point-virgule, ce qui va résoudre notre problème. diff --git a/FRENCH/src/ch03-04-comments.md b/FRENCH/src/ch03-04-comments.md new file mode 100644 index 0000000000..68274ce132 --- /dev/null +++ b/FRENCH/src/ch03-04-comments.md @@ -0,0 +1,117 @@ + + +## Les commentaires + + + +Tous les développeurs s'efforcent de rendre leur code facile à comprendre, mais +parfois il est nécessaire d'écrire des explications supplémentaires. +Dans ce cas, les développeurs laissent des notes, appelées *commentaires*, dans +leur code source que le compilateur va ignorer mais qui peuvent être utiles +pour les personnes qui lisent le code source. + + + +Voici un simple commentaire : + +```rust +// hello, world +``` + + + +Avec Rust, les commentaires commencent avec deux barres obliques et continuent +jusqu'à la fin de la ligne. Pour les commentaires qui font plus d'une seule +ligne, vous aurez besoin d'ajouter `//` sur chaque ligne, comme ceci : + + + +```rust +// Donc ici on fait quelque chose de compliqué, tellement long que nous avons +// besoin de plusieurs lignes de commentaires pour le faire ! Heureusement, +// ce commentaire va expliquer ce qui se passe. +``` + + + +Les commentaires peuvent aussi être aussi ajoutés à la fin d'une ligne qui +contient du code : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let nombre_chanceux = 7; // Je me sens chanceux aujourd'hui +} +``` + + + +Mais parfois, vous pourrez les voir utilisés de cette manière, avec le +commentaire sur une ligne séparée au-dessus du code qu'il annote : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + // Je me sens chanceux aujourd'hui + let nombre_chanceux = 7; +} +``` + + + +Rust a aussi un autre type de commentaire, les commentaires de documentation, +que nous aborderons au chapitre 14. diff --git a/FRENCH/src/ch03-05-control-flow.md b/FRENCH/src/ch03-05-control-flow.md new file mode 100644 index 0000000000..9ba2f10f2a --- /dev/null +++ b/FRENCH/src/ch03-05-control-flow.md @@ -0,0 +1,1207 @@ + + +## Les structures de contrôle + + + +Choisir d'exécuter ou non du code selon qu'une condition est vérifiée et +choisir d'exécuter du code de façon répétée tant qu'une condition est vérifiée +sont des constructions élémentaires dans la plupart des langages de +programmation. Les structures de contrôle les plus courantes en Rust sont les +expressions `if` et les boucles. + + + +### Les expressions `if` + + + +Une expression `if` vous permet de diviser votre code en fonction de conditions. +Vous précisez une condition et vous choisissez ensuite : “Si cette condition est +remplie, alors exécuter ce bloc de code. Si la condition n'est pas remplie, +ne pas exécuter ce bloc de code.” + + + +Créez un nouveau projet appelé *branches* dans votre dossier *projects* pour +découvrir les expressions `if`. Dans le fichier *src/main.rs*, écrivez ceci : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let nombre = 3; + + if nombre < 5 { + println!("La condition est vérifiée"); + } else { + println!("La condition n'est pas vérifiée"); + } +} +``` + + + +Une expression `if` commence par le mot-clé `if`, suivi d'une condition. +Dans notre cas, la condition vérifie si oui ou non la variable `nombre` a une +valeur inférieure à 5. Le bloc de code que nous voulons exécuter si la condition +est vérifiée est placé immédiatement après la condition entre des accolades. +Les blocs de code associés à une condition dans une expression `if` sont parfois +appelés des *branches*, exactement comme les branches dans les expressions +`match` que nous avons vu dans la section [“Comparer le nombre saisi au +nombre secret”][comparing-the-guess-to-the-secret-number] du +chapitre 2. + + + +Éventuellement, vous pouvez aussi ajouter une expression `else`, ce que nous +avons fait ici, pour préciser un bloc alternatif de code qui sera exécuté dans +le cas où la condition est fausse (elle n'est pas vérifiée). Si +vous ne renseignez pas d'expression `else` et que la condition n'est pas +vérifiée, le programme va simplement sauter le bloc de `if` et passer au +prochain morceau de code. + + + +Essayez d'exécuter ce code ; vous verrez ceci : + + + +```text +$ cargo run + Compiling branches v0.1.0 (file:///projects/branches) + Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs + Running `target/debug/branches` +La condition est vérifiée +``` + + + +Essayons de changer la valeur de `nombre` pour une valeur qui rend la condition +non vérifiée pour voir ce qui se passe : + + + +```rust,ignore +let nombre = 7; +``` + + + +Exécutez à nouveau le programme, et regardez le résultat : + + + +```text +$ cargo run + Compiling branches v0.1.0 (file:///projects/branches) + Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs + Running `target/debug/branches` +La condition n'est pas vérifiée +``` + + + +Il est aussi intéressant de noter que la condition dans ce code *doit* être un +`bool`. Si la condition n'est pas un `bool`, nous aurons une erreur. Par +exemple, essayez d'exécuter le code suivant : + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +fn main() { + let nombre = 3; + + if nombre { + println!("Le nombre était trois"); + } +} +``` + + + +La condition `if` vaut `3` cette fois, et Rust lève une erreur : + + + +```text +error[E0308]: mismatched types + -- > src/main.rs:4:8 + | +4 | if nombre { + | ^^^^^^ expected bool, found integer + | + = note: expected type `bool` + found type `{integer}` +``` + + + +Cette erreur explique que Rust attendait un `bool` mais a obtenu un entier +*(integer)*. Contrairement à des langages comme Ruby et JavaScript, Rust +ne va pas essayer de convertir automatiquement les types non booléens en +booléens. Vous devez être précis et toujours fournir un booléen à la condition +d'un `if`. Si nous voulons que le bloc de code du `if` soit exécuté quand le +nombre est différent de `0`, par exemple, nous pouvons changer l'expression `if` +par la suivante : + + + +Fichier: src/main.rs + + + +```rust +fn main() { + let nombre = 3; + + if nombre != 0 { + println!("Le nombre valait autre chose que zéro"); + } +} +``` + + + +Exécuter ce code va bien afficher `Le nombre valait autre chose que zéro`. + + + +#### Gérer plusieurs conditions avec `else if` + + + +Vous pouvez utiliser plusieurs conditions en combinant `if` et `else` dans une +expression `else if`. Par exemple : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let nombre = 6; + + if nombre % 4 == 0 { + println!("Le nombre est divisible par 4"); + } else if nombre % 3 == 0 { + println!("Le nombre est divisible par 3"); + } else if nombre % 2 == 0 { + println!("Le nombre est divisible par 2"); + } else { + println!("Le nombre n'est pas divisible par 4, 3 ou 2"); + } +} +``` + + + +Ce programme peut choisir entre quatre chemins différents. Après l'avoir +exécuté, vous devriez voir le résultat suivant : + + + +```text +$ cargo run + Compiling branches v0.1.0 (file:///projects/branches) + Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs + Running `target/debug/branches` +Le nombre est divisible par 3 +``` + + + +Quand ce programme s'exécute, il vérifie chaque expression `if` à tour de rôle +et exécute le premier bloc dont la condition est vérifiée. Notez que même si 6 +est divisible par 2, nous ne voyons pas le message `Le nombre est divisible par +2`, ni le message `Le nombre n'est pas divisible par 4, 3 ou 2` du bloc `else`. +C'est parce que Rust n'exécute que le bloc de la première condition vérifiée, +et dès lors qu'il en a trouvé une, il ne va pas chercher à vérifier les +suivantes. + + + +Utiliser trop d'expressions `else if` peut encombrer votre code, donc si vous +en avez plus d'une, vous devriez envisager de remanier votre code. Le chapitre 6 +présente une construction puissante appelée `match` pour de tels cas. + + + +#### Utiliser `if` dans une instruction `let` + + + +Comme `if` est une expression, nous pouvons l'utiliser à droite d'une +instruction `let`, comme dans l'encart 3-2. + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let condition = true; + let nombre = if condition { + 5 + } else { + 6 + }; + + println!("La valeur du nombre est : {}", nombre); +} +``` + + + +Encart 3-2 : assigner le résultat d'une expression `if` à +une variable + + + +La variable `nombre` va avoir la valeur du résultat de l'expression `if`. +Exécutez ce code pour découvrir ce qui va se passer : + + + +```text +$ cargo run + Compiling branches v0.1.0 (file:///projects/branches) + Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs + Running `target/debug/branches` +La valeur du nombre est : 5 +``` + + + +Souvenez-vous que les blocs de code s'exécutent jusqu'à la dernière expression +qu'ils contiennent, et que les nombres tout seuls sont aussi des expressions. +Dans notre cas, la valeur de toute l'expression `if` dépend de quel bloc de code +elle va exécuter. Cela veut dire que chaque valeur qui peut être le résultat de +chaque branche du `if` doivent être du même type ; dans l'encart 3-2, les +résultats des branches `if` et `else` sont tous deux des entiers `i32`. Si +les types ne sont pas identiques, comme dans l'exemple suivant, nous allons +obtenir une erreur : + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +fn main() { + let condition = true; + + let nombre = if condition { + 5 + } else { + "six" + }; + + println!("La valeur du nombre est : {}", nombre); +} +``` + + + +Lorsque nous essayons de compiler ce code, nous obtenons une erreur. Les +branches `if` et `else` ont des types de valeurs qui ne sont pas compatibles, et +Rust indique exactement où trouver le problème dans le programme : + + + +```text +error[E0308]: if and else have incompatible types + -- > src/main.rs:4:18 + | +4 | let nombre = if condition { + | __________________^ +5 | | 5 +6 | | } else { +7 | | "six" +8 | | }; + | |_____^ expected integer, found &str + | + = note: expected type `{integer}` + found type `&str` +``` + + + +L'expression dans le bloc `if` donne un entier, et l'expression dans le bloc +`else` donne une chaîne de caractères. Ceci ne fonctionne pas car les variables +doivent avoir un seul type. Rust a besoin de savoir de quel type est la variable +`nombre` au moment de la compilation, assurément, afin de vérifier au moment +de la compilation que son type est valable n'importe où nous utilisons `nombre`. +Rust ne serait pas capable de faire cela si le type de `nombre` était déterminé +uniquement à l'exécution ; car le compilateur deviendrait plus complexe et nous +donnerait moins de garanties sur le code s'il devait prendre en compte tous les +types hypothétiques pour une variable. + + + +### Les répétitions avec les boucles + + + +Il est parfois utile d'exécuter un bloc de code plus d'une seule fois. Dans ce +but, Rust propose plusieurs types de *boucles*. Une boucle parcourt le code à +l'intérieur du corps de la boucle jusqu'à la fin et recommence immédiatement du +début. Pour tester les boucles, créons un nouveau projet appelé *loops*. + + + +Rust a trois types de boucles : `loop`, `while`, et `for`. Essayons chacune +d'elles. + + + +#### Répéter du code avec `loop` + + + +Le mot-clé `loop` demande à Rust d'exécuter un bloc de code encore et encore +jusqu'à l'infini ou jusqu'à ce que vous lui demandiez explicitement de +s'arrêter. + + + +Par exemple, changez le fichier *src/main.rs* dans votre dossier *loops* comme +ceci : + + + +Fichier : src/main.rs + + + +```rust,ignore +fn main() { + loop { + println!("À nouveau !"); + } +} +``` + + + +Quand nous exécutons ce programme, nous voyons `À nouveau !` s'afficher encore +et encore en continu jusqu'à ce qu'on arrête le programme manuellement. La +plupart des terminaux utilisent un raccourci clavier, +ctrl-c, pour arrêter un programme qui est bloqué dans une boucle infinie. +Essayons cela : + + + +```text +$ cargo run + Compiling loops v0.1.0 (file:///projects/loops) + Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs + Running `target/debug/loops` +À nouveau ! +À nouveau ! +À nouveau ! +À nouveau ! +^CÀ nouveau ! +``` + + + +Le symbole `^C` représente le moment où vous avez appuyé sur +ctrl-c. Vous devriez voir ou non le texte +`À nouveau !` après le `^C`, en fonction de là où la boucle en était dans votre +code quand elle a reçu le signal d'arrêt. + + + +Heureusement, Rust fournit un autre moyen, plus fiable, de sortir d'une boucle. +Vous pouvez ajouter le mot-clé `break` à l'intérieur de la boucle pour demander +au programme d'arrêter la boucle. Souvenez-vous que nous avions fait ceci dans +le jeu de devinettes, dans la section [“Arrêter le programme après avoir +gagné”][quitting-after-a-correct-guess] du chapitre 2 afin de +quitter le programme quand l'utilisateur gagne le jeu en devinant le bon nombre. + + + +#### Retourner des valeurs d'une boucle + + + +L'une des utilisations d'une boucle `loop` est de réessayer une opération qui +peut échouer, comme vérifier si une tâche a terminé son travail. Cependant, vous +aurez peut-être besoin de passer le résultat de l'opération au reste de votre +code. Pour ce faire, vous pouvez ajouter la valeur que vous voulez retourner +après l'expression `break` que vous utilisez pour stopper la boucle ; cette +valeur sera retournée de la boucle pour que vous puissiez l'utiliser, comme +ci-dessous : + + + +```rust +fn main() { + let mut compteur = 0; + + let resultat = loop { + compteur += 1; + + if compteur == 10 { + break compteur * 2; + } + }; + + println!("Le résultat est {}", resultat); +} +``` + + + +Avant la boucle, nous déclarons une variable avec le nom `compteur` et nous +l'initialisons à `0`. Ensuite, nous déclarons une variable `resultat` pour +stocker la valeur retournée de la boucle. À chaque itération de la boucle, nous +ajoutons `1` à la variable `compteur`, et ensuite nous vérifions si le compteur +est égal à `10`. Lorsque c'est le cas, nous utilisons le mot-clé `break` avec la +valeur `compteur * 2`. Après la boucle, nous utilisons un point-virgule pour +terminer l'instruction qui assigne la valeur à `resultat`. Enfin, nous +affichons la valeur de `resultat`, qui est 20 dans ce cas-ci. + + + +#### Les boucles conditionnelles avec `while` + + + +Il est souvent utile pour un programme d'évaluer une condition dans une boucle. +Tant que la condition est vraie, la boucle tourne. Quand la condition arrête +d'être vraie, le programme appelle `break`, ce qui arrête la boucle. Ce type de +boucle peut être implémenté en combinant `loop`, `if`, `else` et `break` ; vous +pouvez essayer de le faire, si vous voulez. + + + +Cependant, cette utilisation est si fréquente que Rust a une construction pour +cela, intégrée dans le langage, qui s'appelle une boucle `while`. L'encart 3-3 +utilise `while` : le programme va boucler trois fois, en décrémentant à chaque +fois, et ensuite, après la boucle, il va afficher un message et se fermer. + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let mut nombre = 3; + + while nombre != 0 { + println!("{} !", nombre); + + nombre -= 1; + } + + println!("DÉCOLLAGE !!!"); +} +``` + + + +Encart 3-3: utiliser une boucle `while` pour exécuter du +code tant qu'une condition est vraie + + + +Cette construction élimine beaucoup d'imbrications qui seraient nécessaires si +vous utilisiez `loop`, `if`, `else` et `break`, et c'est aussi plus clair. Tant +que la condition est vraie, le code est exécuté ; sinon, il quitte la boucle. + + + +#### Boucler dans une collection avec `for` + + + +Vous pouvez utiliser la construction `while` pour itérer sur les +éléments d'une collection, comme les tableaux. Par exemple, analysons l'encart +3-4. + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let a = [10, 20, 30, 40, 50]; + let mut indice = 0; + + while indice < 5 { + println!("La valeur est : {}", a[indice]); + + indice += 1; + } +} +``` + + + +Encart 3-4 : itération sur les éléments d'une collection +en utilisant une boucle `while` + + + +Ici, le code parcourt le tableau élément par élément. +Il commence à l'indice `0`, et ensuite boucle jusqu'à ce qu'il atteigne l'indice +final du tableau (ce qui correspond au moment où la condition `index < 5` n'est +plus vraie). Exécuter ce code va afficher chaque élément du tableau : + + + +```text +$ cargo run + Compiling loops v0.1.0 (file:///projects/loops) + Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs + Running `target/debug/loops` +La valeur est : 10 +La valeur est : 20 +La valeur est : 30 +La valeur est : 40 +La valeur est : 50 +``` + + + +Les cinq valeurs du tableau s'affichent toutes dans le terminal, comme attendu. +Même si `indice` va atteindre la valeur `5` à un moment, la boucle arrêtera de +s'exécuter avant d'essayer de récupérer une sixième valeur du tableau. + + + +Mais cette approche pousse à l'erreur ; nous pourrions faire paniquer le +programme si l'indice est trop grand. De plus, c'est lent, car le compilateur +ajoute du code à l'exécution pour effectuer des vérifications sur chaque élément +à chaque itération de la boucle. + + + +Pour une alternative plus concise, vous pouvez utiliser une boucle `for` et +exécuter du code pour chaque élément dans une collection. Une boucle `for` +s'utilise comme dans le code de l'encart 3-5. + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let a = [10, 20, 30, 40, 50]; + + for element in a.iter() { + println!("La valeur est : {}", element); + } +} +``` + + + +Encart 3-5 : itérer sur chaque élément d'une collection +en utilisant une boucle `for` + + + +Lorsque nous exécutons ce code, nous obtenons les mêmes messages que dans +l'encart 3-4. Mais ce qui est plus important, c'est que nous avons amélioré la +sécurité de notre code et éliminé le risque de bogues qui pourraient survenir +si on dépassait la fin du tableau, ou si on n'allait pas jusqu'au bout +et qu'on ratait quelques éléments. + + + +Par exemple, dans le code de l'encart 3-4, si vous enlevez un élément du +tableau `a` mais que vous oubliez de mettre à jour la condition tel que +`while indice < 4`, le code va paniquer. En utilisant la boucle `for`, vous +n'aurez pas à vous rappeler de changer le code si vous changez le nombre de +valeurs dans le tableau. + + + +La sécurité et la concision de la boucle `for` en font la construction de boucle +la plus utilisée avec Rust. Même dans des situations dans lesquelles vous +voudriez exécuter du code plusieurs fois, comme l'exemple du décompte qui +utilisait une boucle `while` dans l'encart 3-3, la plupart des Rustacés +utiliseraient une boucle `for`. Il faut pour cela utiliser un intervalle +`Range`, qui est un type fourni par la bibliothèque standard qui génère dans +l'ordre tous les nombres compris entre un certain nombre et un autre nombre. + + + +Voici ce que le décompte aurait donné en utilisant une boucle `for` et une autre +méthode que nous n'avons pas encore vue, `rev`, qui inverse l'intervalle : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + for nombre in (1..4).rev() { + println!("{} !", nombre); + } + println!("DÉCOLLAGE !!!"); +} +``` + + + +Ce code est un peu plus sympa, non ? + + + +## Résumé + + + +Vous y êtes arrivé ! C'était un chapitre important : vous avez appris les +variables, les types scalaires et composés, les fonctions, les commentaires, les +expressions `if`, et les boucles ! Si vous voulez pratiquer un peu les concepts +abordés dans ce chapitre, voici quelques programmes que vous pouvez essayer de +créer : + + + +* Convertir des températures entre les degrés Fahrenheit et Celsius. +* Générer le *n*-ième nombre de Fibonacci. +* Afficher les paroles de la chanson de Noël *The Twelve Days of Christmas* en + profitant de l'aspect répétitif de la chanson. + + + +Quand vous serez prêt à aller plus loin, nous aborderons une notion de Rust +qui n'existe *pas* dans les autres langages de programmation : la possession +*(ownership)*. + + + +[comparing-the-guess-to-the-secret-number]: +ch02-00-guessing-game-tutorial.html#comparer-le-nombre-saisi-au-nombre-secret +[quitting-after-a-correct-guess]: +ch02-00-guessing-game-tutorial.html#arrêter-le-programme-après-avoir-gagné diff --git a/FRENCH/src/ch04-00-understanding-ownership.md b/FRENCH/src/ch04-00-understanding-ownership.md new file mode 100644 index 0000000000..73dbfae0c1 --- /dev/null +++ b/FRENCH/src/ch04-00-understanding-ownership.md @@ -0,0 +1,20 @@ + + +# Comprendre la possession + + + +La possession (*ownership*) est la fonctionnalité la plus remarquable de Rust, +et elle permet à Rust de garantir la sécurité de la mémoire sans avoir besoin +d'un ramasse-miettes (*garbage collector*). Par conséquent, il est important de +comprendre comment la possession fonctionne en Rust. Dans ce chapitre, nous +aborderons la possession, ainsi que d'autres fonctionnalités associées : +l'emprunt, les *slices* et la façon dont Rust agence les données en mémoire. diff --git a/FRENCH/src/ch04-01-what-is-ownership.md b/FRENCH/src/ch04-01-what-is-ownership.md new file mode 100644 index 0000000000..34f0df038c --- /dev/null +++ b/FRENCH/src/ch04-01-what-is-ownership.md @@ -0,0 +1,1320 @@ + + +## Qu'est-ce que la possession ? + + + +La principale spécificité de Rust est *la possession*. Bien que cette +fonctionnalité soit simple à expliquer, elle a de profondes conséquences sur le +reste du langage. + + + +Tous les programmes doivent gérer la façon dont ils utilisent la mémoire +lorsqu'ils s'exécutent. Certains langages ont un ramasse-miettes qui scrute +constamment la mémoire qui n'est plus utilisée pendant qu'il s'exécute ; dans +d'autres langages, le développeur doit explicitement allouer et libérer la +mémoire. Rust adopte une troisième approche : la mémoire est gérée avec un +système de possession qui repose sur un jeu de règles que le compilateur vérifie +au moment de la compilation. Aucune des fonctionnalités de possession ne +ralentit votre programme à l'exécution. + + + +Comme la possession est un nouveau principe pour de nombreux développeurs, +cela prend un certain temps pour s'y familiariser. La bonne nouvelle est que +plus vous devenez expérimenté avec Rust et ses règles de possession, plus vous +développerez naturellement du code sûr et efficace. Gardez bien cela à +l'esprit ! + + + +Lorsque vous comprendrez la possession, vous aurez des bases solides pour +comprendre les fonctionnalités qui font la particularité de Rust. Dans ce +chapitre, vous allez apprendre la possession en pratiquant avec plusieurs +exemples qui se concentrent sur une structure de données très courante : les +chaînes de caractères. + + + +> ### La pile et le tas +> +> Dans de nombreux langages, il n'est pas nécessaire de se préoccuper de la +> pile (*stack*) et du tas (*heap*). Mais dans un langage de programmation +> système comme Rust, qu'une donnée soit sur la pile ou sur le tas influe +> sur le comportement du langage et explique pourquoi nous devons faire certains +> choix. Nous décrirons plus loin dans ce chapitre comment la possession +> fonctionne vis-à-vis de la pile et du tas, voici donc une brève explication au +> préalable. +> +> La pile et le tas sont tous les deux des emplacements de la mémoire qui +> sont à disposition de votre code lors de son exécution, mais sont organisés de +> façon différente. La pile enregistre les valeurs dans l'ordre qu'elle les +> reçoit et enlève les valeurs dans l'autre sens. C'est ce que l'on appelle le +> principe de *dernier entré, premier sorti*. C'est comme une pile d'assiettes : +> quand vous ajoutez des nouvelles assiettes, vous les déposez sur le dessus de +> la pile, et quand vous avez besoin d'une assiette, vous en prenez une sur le +> dessus. Ajouter ou enlever des assiettes au milieu ou en bas ne serait pas +> aussi efficace ! Ajouter une donnée sur la pile se dit *empiler* et en retirer +> une se dit *dépiler*. +> +> Toutes les données stockées dans la pile doivent avoir une taille connue et +> fixe. Les données avec une taille inconnue au moment de la compilation ou une +> taille qui peut changer doivent plutôt être stockées sur le tas. Le tas est +> moins bien organisé : lorsque vous ajoutez des données sur le tas, vous +> demandez une certaine quantité d'espace mémoire. Le système d'exploitation va +> trouver un emplacement dans le tas qui est suffisamment grand, va le marquer +> comme étant en cours d'utilisation, et va retourner un *pointeur*, qui est +> l'adresse de cet emplacement. Cette procédure est appelée *allocation sur le +> tas*, ce qu'on abrège parfois en *allocation* tout court. L'ajout de valeurs +> sur la pile n'est pas considéré comme une allocation. Comme le pointeur a une +> taille connue et fixe, on peut stocker ce pointeur sur la pile, mais quand on +> veut la vraie donnée, il faut suivre le pointeur. +> +> C'est comme si vous vouliez manger au restaurant. Quand vous entrez, vous +> indiquez le nombre de personnes dans votre groupe, et le personnel trouve une +> table vide qui peut recevoir tout le monde, et vous y conduit. Si quelqu'un +> dans votre groupe arrive en retard, il peut leur demander où vous êtes assis +> pour vous rejoindre. +> +> Empiler sur la pile est plus rapide qu'allouer sur le tas car le système +> d'exploitation ne va jamais avoir besoin de chercher un emplacement pour y +> stocker les nouvelles données ; il le fait toujours au sommet de la pile. En +> comparaison, allouer de la place sur le tas demande plus de travail, car le +> système d'exploitation doit d'abord trouver un espace assez grand pour stocker +> les données et mettre à jour son suivi pour préparer la prochaine allocation. +> +> Accéder à des données dans le tas est plus lent que d'accéder aux données sur +> la pile car nous devons suivre un pointeur pour les obtenir. Les processeurs +> modernes sont plus rapides s'ils se déplacent moins dans la mémoire. Pour +> continuer avec notre analogie, imaginez un serveur dans un restaurant qui +> prend les commandes de nombreuses tables. C'est plus efficace de récupérer +> toutes les commandes à une seule table avant de passer à la table suivante. +> Prendre une commande à la table A, puis prendre une commande à la table B, +> puis ensuite une autre à la table A, puis une autre à la table B serait un +> processus bien plus lent. De la même manière, un processeur sera plus efficace +> dans sa tâche s'il travaille sur des données qui sont proches les unes des +> autres (comme c'est le cas sur la pile) plutôt que si elles sont plus +> éloignées (comme cela peut être le cas sur le tas). Allouer une grande +> quantité de mémoire sur le tas peut aussi prendre beaucoup de temps. +> +> Quand notre code utilise une fonction, les valeurs passées à la fonction +> (incluant, potentiellement, des pointeurs de données sur le tas) et les +> variables locales à la fonction sont déposées sur la pile. Quand l'utilisation +> de la fonction est terminée, ces données sont retirées de la pile. +> +> La possession nous aide à ne pas nous préoccuper de faire attention à quelles +> parties du code utilisent quelles données sur le tas, de minimiser la +> quantité de données en double sur le tas, ou encore de veiller à libérer les +> données inutilisées sur le tas pour que nous ne soyons pas à court d'espace. +> Quand vous aurez compris la possession, vous n'aurez plus besoin de vous +> préoccuper de la pile et du tas très souvent, mais savoir que la possession +> existe pour gérer les données du tas peut vous aider à comprendre pourquoi +> elle fonctionne de cette manière. + + + +### Les règles de la possession + + + +Tout d'abord, définissons les règles de la possession. Gardez à l'esprit ces +règles pendant que nous travaillons sur des exemples qui les illustrent : + + + +* Chaque valeur en Rust a une variable qui s'appelle son *propriétaire*. +* Il ne peut y avoir qu'un seul propriétaire à la fois. +* Quand le propriétaire sortira de la portée, la valeur sera supprimée. + + + +### Portée de la variable + + + +Nous avons déjà vu un exemple de programme Rust au chapitre 2. Maintenant +que nous avons vu la syntaxe Rust de base, nous n'allons plus ajouter tout le +code du style `fn main() {` dans les exemples, donc si vous voulez reproduire +les exemples, vous devrez les mettre manuellement dans une fonction `main`. Par +conséquent, nos exemples seront plus concis, nous permettant de nous concentrer +sur les détails de la situation plutôt que sur du code normalisé. + + + +Pour le premier exemple de possession, nous allons analyser la *portée* de +certaines variables. Une portée est une zone dans un programme dans laquelle un +élément est en vigueur. Imaginons que nous ayons la variable suivante : + +```rust +let s = "hello"; +``` + + + +La variable `s` fait référence à un littéral de chaîne de caractères, où la +valeur de la chaîne est codée en dur dans notre programme. La variable est en +vigueur à partir du moment où elle est déclarée jusqu'à la fin de la *portée* +actuelle. L'encart 4-1 a des commentaires pour indiquer quand la variable `s` +est en vigueur : + + + +```rust +{ // s n'est pas en vigueur ici, elle n'est pas encore déclarée + let s = "hello"; // s est en vigueur à partir de ce point + + // on fait des choses avec s ici +} // cette portée est maintenant terminée, et s n'est plus en vigueur +``` + + + +Encart 4-1 : Une variable et la portée dans laquelle elle +est en vigueur. + + + +Autrement dit, il y a ici deux étapes importantes : + + + +* Quand `s` rentre *dans la portée*, elle est en vigueur. +* Cela reste ainsi jusqu'à ce qu'elle *sort de la portée*. + + + +Pour le moment, la relation entre les portées et les conditions pour lesquelles +les variables sont en vigueur sont similaires à d'autres langages de +programmation. Maintenant, nous allons aller plus loin en y ajoutant le type +`String`. + + + +### Le type `String` + + + +Pour illustrer les règles de la possession, nous avons besoin d'un type de +donnée qui est plus complexe que ceux que nous avons rencontrés dans la section +[“Types de données”][data-types] du chapitre 3. Les types que +nous avons vus précédemment sont tous stockés sur la pile et sont retirés de la +pile quand ils sortent de la portée, mais nous voulons expérimenter le stockage +de données sur le tas et découvrir comment Rust sait quand il doit nettoyer ces +données. + + + +Nous allons utiliser ici `String` pour l'exemple et nous concentrer sur les +caractéristiques de `String` qui sont liées à la possession. Ces aspects +s'appliquent également à d'autres types de données complexes, qu'ils soient +fournis par la bibliothèque standard ou qu'ils soient créés par vous. Nous +verrons `String` plus en détail dans le chapitre 8. + + + +Nous avons déjà vu les littéraux de chaînes de caractères, quand une valeur de +chaîne est codée en dur dans notre programme. Les littéraux de chaînes sont +pratiques, mais ils ne conviennent pas toujours à tous les cas où on veut +utiliser du texte. Une des raisons est qu'ils sont immuables. Une autre raison +est qu'on ne connaît pas forcément le contenu des chaînes de caractères quand +nous écrivons notre code : par exemple, comment faire si nous voulons récupérer +du texte saisi par l'utilisateur et l'enregistrer ? Pour ces cas-ci, Rust a un +second type de chaîne de caractères, `String`. Ce type est alloué sur le tas et +est ainsi capable de stocker une quantité de texte qui nous est inconnue au +moment de la compilation. Vous pouvez créer une `String` à partir d'un littéral +de chaîne de caractères en utilisant la fonction `from`, comme ceci : + +```rust +let s = String::from("hello"); +``` + + + +Le double deux-points (`::`) est un opérateur qui nous permet d'appeler cette +fonction spécifique dans l'espace de nom du type `String` plutôt que d'utiliser +un nom comme `string_from`. Nous verrons cette syntaxe plus en détail dans la +section [“Syntaxe de méthode”][method-syntax] du chapitre 5 et +lorsque nous aborderons les espaces de noms dans la section +[“Les chemins pour désigner un élément dans l'arborescence de +module”][paths-module-tree] du chapitre 7. + + + +Ce type de chaîne de caractères *peut* être mutable : + + + +```rust +let mut s = String::from("hello"); + +s.push_str(", world!"); // push_str() ajoute un littéral de chaîne dans une String + +println!("{}", s); // Cela va afficher `hello, world!` +``` + + + +Donc, quelle est la différence ici ? Pourquoi `String` peut être mutable, mais +pourquoi les littéraux de chaînes ne peuvent pas l'être ? La différence +se trouve dans la façon dont ces deux types travaillent avec la mémoire. + + + +### Mémoire et allocation + + + +Dans le cas d'un littéral de chaîne de caractères, nous connaissons le contenu +au moment de la compilation donc le texte est codé en dur directement dans +l'exécutable final. Voilà pourquoi ces littéraux de chaînes de caractères sont +performants et rapides. Mais ces caractéristiques viennent de leur immuabilité. +Malheureusement, on ne peut pas accorder une grosse région de mémoire dans le +binaire pour chaque morceau de texte qui n'a pas de taille connue au moment de +la compilation et dont la taille pourrait changer pendant l'exécution de ce +programme. + + + +Avec le type `String`, pour nous permettre d'avoir un texte mutable et qui peut +s'agrandir, nous devons allouer une quantité de mémoire sur le tas, inconnue +au moment de la compilation, pour stocker le contenu. Cela signifie que : + + + +* La mémoire doit être demandée auprès du système d'exploitation lors de + l'exécution. +* Nous avons besoin d'un moyen de rendre cette mémoire au système + d'exploitation lorsque nous aurons fini d'utiliser notre `String`. + + + +Nous nous occupons de ce premier point : quand nous appelons `String::from`, son +implémentation demande la mémoire dont elle a besoin. C'est pratiquement +toujours ainsi dans la majorité des langages de programmation. + + + +Cependant, le deuxième point est différent. Dans des langages avec un +*ramasse-miettes*, le ramasse-miettes surveille et nettoie la mémoire qui n'est +plus utilisée, sans que nous n'ayons à nous en préoccuper. Sans un +ramasse-miettes, c'est de notre responsabilité d'identifier quand cette mémoire +n'est plus utilisée et d'appeler du code pour explicitement la libérer, comme +nous l'avons fait pour la demander auparavant. Historiquement, faire ceci +correctement a toujours été une difficulté pour les développeurs. Si nous +oublions de le faire, nous allons gaspiller de la mémoire. Si nous le faisons +trop tôt, nous allons avoir une variable invalide. Si nous le faisons deux fois, +cela produit aussi un bogue. Nous devons associer exactement un `allocate` avec +exactement un `free`. + + + +Rust prend un chemin différent : la mémoire est automatiquement libérée dès +que la variable qui la possède sort de la portée. Voici une version de notre +exemple de portée de l'encart 4-1 qui utilise une `String` plutôt qu'un littéral +de chaîne de caractères : + + + +```rust +{ + let s = String::from("hello"); // s est en vigueur à partir de ce point + + // on fait des choses avec s ici +} // cette portée est désormais terminée, et s + // n'est plus en vigueur maintenant +``` + + + +Il y a un moment naturel où nous devons rendre la mémoire de notre +`String` au système d'exploitation : quand `s` sort de la portée. Quand une +variable sort de la portée, Rust appelle une fonction spéciale pour nous. Cette +fonction s'appelle `drop`, et c'est dans celle-ci que l'auteur de `String` a pu +mettre le code pour libérer la mémoire. Rust appelle automatiquement `drop` à +l'accolade fermante `}`. + + + +> Remarque : en C++, cette façon de libérer des ressources à la fin de la +> durée de vie d'un élément est parfois appelée *l'acquisition d'une ressource +> est une initialisation (RAII)*. La fonction `drop` de Rust vous sera familière +> si vous avez déjà utilisé des techniques de RAII. + + + +Cette façon de faire a un impact profond sur la façon dont le code Rust est +écrit. Cela peut sembler simple dans notre cas, mais le comportement du code +peut être surprenant dans des situations plus compliquées où nous voulons +avoir plusieurs variables utilisant des données que nous avons affectées sur le +tas. Examinons une de ces situations dès à présent. + + + +#### Les interactions entre les variables et les données : le déplacement + + + +Plusieurs variables peuvent interagir avec les mêmes données de différentes +manières en Rust. Regardons un exemple avec un entier dans l'encart 4-2 : + +```rust +let x = 5; +let y = x; +``` + + + +Encart 4-2 : Assigner l'entier de la variable `x` à `y` + + + + +Nous pouvons probablement deviner ce que ce code fait : “Assigner la valeur `5` +à `x` ; ensuite faire une copie de cette valeur de `x` et l'assigner à `y`.” +Nous avons maintenant deux variables, `x` et `y`, et chacune vaut `5`. C'est +effectivement ce qui se passe, car les entiers sont des valeurs simples avec une +taille connue et fixée, et ces deux valeurs `5` sont stockées sur la pile. + + + +Maintenant, essayons une nouvelle version avec `String` : + +```rust +let s1 = String::from("hello"); +let s2 = s1; +``` + + + +Cela ressemble beaucoup au code précédent, donc nous allons supposer que cela +fonctionne pareil que précédemment : ainsi, la seconde ligne va faire une copie +de la valeur de `s1` et l'assigner à `s2`. Mais ce n'est pas tout à fait ce +qu'il se passe. + + + +Regardons l'illustration 4-1 pour découvrir ce qui arrive à `String` sous le +capot. Une `String` est constituée de trois éléments, présents sur la gauche : +un pointeur vers la mémoire qui contient le contenu de la chaîne de caractères, +une taille, et une capacité. Ce groupe de données est stocké sur la pile. À +droite, nous avons la mémoire sur le tas qui contient les données. + + + + + +Une string en mémoire + + + +Illustration 4-1 : Représentation en mémoire d'une +`String` qui contient la valeur `"hello"` assignée à `s1`. + + + +La taille est la quantité de mémoire, en octets, que le contenu de la `String` +utilise actuellement. La capacité est la quantité totale de mémoire, en octets, +que la `String` a reçue du système d'exploitation. La différence entre la taille +et la capacité est importante, mais pas pour notre exemple, donc pour l'instant, +ce n'est pas grave d'ignorer la capacité. + + + +Quand nous assignons `s1` à `s2`, les données de la `String` sont copiées, ce +qui veut dire que nous copions le pointeur, la taille et la capacité qui sont +stockés sur la pile. Nous ne copions pas les données stockées sur le tas +auxquelles le pointeur se réfère. Autrement dit, la représentation des données +dans la mémoire ressemble à l'illustration 4-2. + + + + + +s1 et s2 qui pointent vers la même valeur + + + +Illustration 4-2 : Représentation en mémoire de la +variable `s2` qui a une copie du pointeur, de la taille et de la capacité de +`s1` + + + +Cette représentation *n'est pas* comme l'illustration 4-3, qui représenterait la +mémoire si Rust avait aussi copié les données sur le tas. Si Rust faisait ceci, +l'opération `s2 = s1` pourrait potentiellement être très coûteuse en termes de +performances d'exécution si les données sur le tas étaient volumineuses. + + + + + +s1 et s2 à deux endroits + + + +Illustration 4-3 : Une autre possibilité de ce que +pourrait faire `s2 = s1` si Rust copiait aussi les données du tas + + + +Précédemment, nous avons dit que quand une variable sortait de la portée, Rust +appelait automatiquement la fonction `drop` et nettoyait la mémoire sur le tas +allouée pour cette variable. Mais l'illustration 4-2 montre que les deux +pointeurs de données pointeraient au même endroit. C'est un problème : quand +`s2` et `s1` sortent de la portée, elles vont essayer toutes les deux de +libérer la même mémoire. C'est ce qu'on appelle une erreur de *double +libération* et c'est un des bogues de sécurité de mémoire que nous avons +mentionnés précédemment. Libérer la mémoire deux fois peut mener à des +corruptions de mémoire, ce qui peut potentiellement mener à des vulnérabilités +de sécurité. + + + +Pour garantir la sécurité de la mémoire, il y a un autre petit détail qui se +produit dans cette situation avec Rust. Plutôt qu'essayer de copier la mémoire +allouée, Rust considère que `s1` n'est plus en vigueur et donc, Rust n'a pas +besoin de libérer quoi que ce soit lorsque `s1` sort de la portée. Regardez ce +qu'il se passe quand vous essayez d'utiliser `s1` après que `s2` est créé, +cela ne va pas fonctionner : + +```rust,ignore,does_not_compile +let s1 = String::from("hello"); +let s2 = s1; + +println!("{}, world!", s1); +``` + + + +Vous allez avoir une erreur comme celle-ci, car Rust vous défend d'utiliser la +référence qui n'est plus en vigueur : + +```text +error[E0382]: use of moved value: `s1` + -- > src/main.rs:5:28 + | +3 | let s2 = s1; + | -- value moved here +4 | +5 | println!("{}, world!", s1); + | ^^ value used here after move + | + = note: move occurs because `s1` has type `std::string::String`, which does + not implement the `Copy` trait +``` + + + +Si vous avez déjà entendu parler de *copie superficielle* et de *copie +profonde* en utilisant d'autres langages, l'idée de copier le pointeur, la +taille et la capacité sans copier les données peut vous faire penser à de la +copie superficielle. Mais comme Rust neutralise aussi la première variable, au +lieu d'appeler cela une copie superficielle, on appelle cela un *déplacement*. +Ici, nous pourrions dire que `s1` a été *déplacé* dans `s2`. Donc ce qui se +passe réellement est décrit par l'illustration 4-4. + + + + + +s1 déplacé dans s2 + + + +Illustration 4-4 : Représentation de la mémoire après que +`s1` a été neutralisée + + + +Cela résout notre problème ! Avec seulement `s2` en vigueur, quand elle +sortira de la portée, elle seule va libérer la mémoire, et c'est tout. + + + +De plus, cela signifie qu'il y a eu un choix de conception : Rust ne va jamais +créer automatiquement de copie “profonde” de vos données. Par conséquent, toute +copie *automatique* peut être considérée comme peu coûteuse en termes de +performances d'exécution. + + + +#### Les interactions entre les variables et les données : le clonage + + + +Si nous *voulons* faire une copie profonde des données sur le tas d'une +`String`, et pas seulement des données sur la pile, nous pouvons utiliser une +méthode commune qui s'appelle `clone`. Nous aborderons la syntaxe des méthodes +au chapitre 5, mais comme les méthodes sont des outils courants dans de +nombreux langages, vous les avez probablement utilisées auparavant. + + + +Voici un exemple d'utilisation de la méthode `clone` : + +```rust +let s1 = String::from("hello"); +let s2 = s1.clone(); + +println!("s1 = {}, s2 = {}", s1, s2); +``` + + + +Cela fonctionne très bien et c'est ainsi que vous pouvez reproduire le +comportement décrit dans l'illustration 4-3, où les données du tas sont copiées. + + + +Quand vous voyez un appel à `clone`, vous savez que du code arbitraire est +exécuté et que ce code peut être coûteux. C'est un indicateur visuel qu'il se +passe quelque chose de différent. + + + +#### Données uniquement sur la pile : la copie + + + +Il y a un autre détail dont on n'a pas encore parlé. Le code suivant utilise +des entiers, et on en a vu une partie dans l'encart 4-2 ; il fonctionne et +est correct : + +```rust +let x = 5; +let y = x; + +println!("x = {}, y = {}", x, y); +``` + + + +Mais ce code semble contredire ce que nous venons d'apprendre : nous n'avons +pas appelé `clone`, mais `x` est toujours en vigueur et n'a pas été déplacé +dans `y`. + + + +La raison est que les types comme les entiers ont une taille connue au moment de +la compilation et sont entièrement stockés sur la pile, donc la copie des +vraies valeurs est rapide à faire. Cela signifie qu'il n'y a pas de raison que +nous voudrions neutraliser `x` après avoir créé la variable `y`. En d'autres +termes, il n'y a pas ici de différence entre la copie superficielle et profonde, +donc appeler `clone` ne ferait rien d'autre qu'une copie superficielle classique +et on peut s'en passer. + + + +Rust a une annotation spéciale appelée le trait `Copy` que nous pouvons utiliser +sur des types comme les entiers qui sont stockés sur la pile (nous verrons les +traits dans le chapitre 10). Si un type a le trait `Copy`, l'ancienne variable +sera toujours utilisable après avoir été affectée. Rust ne nous autorisera pas à +annoter un type avec le trait `Copy` si ce type, ou un de ses éléments, a +implémenté le trait `Drop`. Si ce type a besoin que quelque chose de spécial se +produise quand la valeur sort de la portée et que nous ajoutons l'annotation +`Copy` sur ce type, nous aurons une erreur au moment de la compilation. Pour +savoir comment ajouter l'annotation `Copy` sur votre type, référez-vous à +[l'annexe C][derivable-traits] sur les traits dérivables. + + + +Donc, quels sont les types qui sont `Copy` ? Vous pouvez regarder dans la +documentation pour un type donné pour vous en assurer, mais de manière générale, +tout groupe de valeur scalaire peut être `Copy`, et tout ce qui ne nécessite pas +d'allocation de mémoire ou tout autre forme de ressource est `Copy`. +Voici quelques types qui sont `Copy` : + + + +* Tous les types d'entiers, comme `u32`. +* Le type booléen, `bool`, avec les valeurs `true` et `false`. +* Tous les types de flottants, comme `f64`. +* Le type de caractère, `char`. +* Les tuples, mais uniquement s'ils contiennent des types qui sont aussi `Copy`. + Par exemple, le `(i32, i32)` est `Copy`, mais pas `(i32, String)`. + + + +### La possession et les fonctions + + + +La syntaxe pour passer une valeur à une fonction est similaire à celle pour +assigner une valeur à une variable. Passer une variable à une fonction va la +déplacer ou la copier, comme l'assignation. L'encart 4-3 est un exemple avec +quelques commentaires qui montrent où les variables rentrent et sortent de la +portée : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let s = String::from("hello"); // s rentre dans la portée. + + prendre_possession(s); // La valeur de s est déplacée dans la fonction… + // … et n'est plus en vigueur à partir d'ici + + let x = 5; // x rentre dans la portée. + + creer_copie(x); // x va être déplacée dans la fonction, + // mais i32 est Copy, donc on peut + // utiliser x ensuite. + +} // Ici, x sort de la portée, puis ensuite s. Mais puisque la valeur de s a + // été déplacée, il ne se passe rien de spécial. + +fn prendre_possession(texte: String) { // texte rentre dans la portée. + println!("{}", texte); +} // Ici, texte sort de la portée et `drop` est appelé. La mémoire est libérée. + +fn creer_copie(entier: i32) { // entier rentre dans la portée. + println!("{}", entier); +} // Ici, entier sort de la portée. Il ne se passe rien de spécial. +``` + + + +Encart 4-3 : Les fonctions avec les possessions et les +portées qui sont commentées + + + +Si on essayait d'utiliser `s` après l'appel à `prendre_possession`, Rust +déclencherait une erreur à la compilation. Ces vérifications statiques +nous protègent des erreurs. Essayez d'ajouter du code au `main` qui utilise `s` +et `x` pour découvrir lorsque vous pouvez les utiliser et lorsque les règles de +la possession vous empêchent de le faire. + + + +### Les valeurs de retour et les portées + + + +Retourner des valeurs peut aussi transférer leur possession. L'encart 4-4 est un +exemple avec des annotations similaires à celles de l'encart 4-3 : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let s1 = donne_possession(); // donne_possession déplace sa valeur de + // retour dans s1 + + let s2 = String::from("hello"); // s2 rentre dans la portée + + let s3 = prend_et_rend(s2); // s2 est déplacée dans + // prend_et_rend, qui elle aussi + // déplace sa valeur de retour dans s3. +} // Ici, s3 sort de la portée et est éliminée. s2 sort de la portée mais a été + // déplacée donc il ne se passe rien. s1 sort aussi de la portée et est + // éliminée. + +fn donne_possession() -> String { // donne_possession va déplacer sa + // valeur de retour dans la + // fonction qui l'appelle. + + let texte = String::from("hello"); // texte rentre dans la portée. + + texte // texte est retournée et + // est déplacée vers le code qui + // l'appelle. +} + +// prend_et_rend va prendre une String et en retourne aussi une. +fn prend_et_rend(texte: String) -> String { // texte rentre dans la portée. + + texte // texte est retournée et déplacée vers le code qui l'appelle. +} +``` + + + +Encart 4-4 : Transferts de possession des valeurs de +retour + + + +La possession d'une variable suit toujours le même schéma à chaque fois : +assigner une valeur à une autre variable la déplace. Quand une variable qui +contient des données sur le tas sort de la portée, la valeur sera nettoyée +avec `drop` à moins que la donnée ait été déplacée pour être possédée par une +autre variable. + + + +Il est un peu fastidieux de prendre la possession puis ensuite de retourner la +possession à chaque fonction. Et qu'est-ce qu'il se passe si nous voulons +qu'une fonction utilise une valeur, mais n'en prenne pas possession ? C'est +assez pénible que tout ce que nous passons doit être retourné si nous voulons +l'utiliser à nouveau, en plus de toutes les données qui découlent du corps +de la fonction que nous voulons aussi récupérer. + + + +Il est possible de retourner plusieurs valeurs à l'aide d'un tuple, comme ceci : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let s1 = String::from("hello"); + + let (s2, taille) = calculer_taille(s1); + + println!("La taille de '{}' est {}.", s2, taille); +} + +fn calculer_taille(s: String) -> (String, usize) { + let taille = s.len(); // len() retourne la taille d'une String. + + (s, taille) +} +``` + + + +Encart 4-5 : Retourner la possession des paramètres + + + +Mais c'est trop laborieux et beaucoup de travail pour un principe qui devrait +être banal. Heureusement pour nous, Rust a une fonctionnalité pour ce principe, +c'est ce qu'on appelle les *références*. + + + + + +[data-types]: ch03-02-data-types.html +[derivable-traits]: appendix-03-derivable-traits.html +[method-syntax]: ch05-03-method-syntax.html +[paths-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html diff --git a/FRENCH/src/ch04-02-references-and-borrowing.md b/FRENCH/src/ch04-02-references-and-borrowing.md new file mode 100644 index 0000000000..8f9a7572f4 --- /dev/null +++ b/FRENCH/src/ch04-02-references-and-borrowing.md @@ -0,0 +1,854 @@ + + +## Les références et l'emprunt + + + +La difficulté avec le code du tuple à la fin de la section précédente est que +nous avons besoin de retourner la `String` au code appelant pour qu'il puisse +continuer à utiliser la `String` après l'appel à `calculer_taille`, car la +`String` a été déplacée dans `calculer_taille`. + + + +Voici comment définir et utiliser une fonction `calculer_taille` qui prend une +*référence* à un objet en paramètre plutôt que de prendre possession de la +valeur : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let s1 = String::from("hello"); + + let long = calculer_taille(&s1); + + println!("La taille de '{}' est {}.", s1, long); +} + +fn calculer_taille(s: &String) -> usize { + s.len() +} +``` + + + +Premièrement, on peut observer que tout le code des *tuples* dans la déclaration +des variables et dans la valeur de retour de la fonction a été enlevé. +Deuxièmement, remarquez que nous passons `&s1` à `calculer_taille`, et que dans +sa définition, nous utilisons `&String` plutôt que `String`. + + + +Ces esperluettes sont des *références*, et elles permettent de vous référer à +une valeur sans en prendre possession. L'illustration 4-5 nous montre cela dans +un schéma. + + + + + +&String s qui pointe vers la String s1 + + + +Illustration 4-5 : Un schéma de la `&String s` qui pointe +vers la `String s1` + + + +> Remarque : l'opposé de la création de références avec `&` est le +> *déréférencement*, qui s'effectue avec l'opérateur de déréférencement, `*`. +> Nous allons voir quelques utilisations de l'opérateur de déréférencement dans +> le chapitre 8 et nous aborderons les détails du déréférencement dans le +> chapitre 15. + + + +Regardons de plus près l'appel à la fonction : + + + +```rust +# fn calculer_taille(s: &String) -> usize { +# s.len() +# } +let s1 = String::from("hello"); + +let long = calculer_taille(&s1); +``` + + + +La syntaxe `&s1` nous permet de créer une référence qui se *réfère* à la valeur +de `s1` mais n'en prend pas possession. Et comme elle ne la possède pas, la +valeur vers laquelle elle pointe ne sera pas libérée quand cette référence +sortira de la portée. + + + +De la même manière, la signature de la fonction utilise `&` pour indiquer que +le type du paramètre `s` est une référence. Ajoutons quelques commentaires +explicatifs : + + + +```rust +fn calculer_taille(s: &String) -> usize { // s est une référence à une String + s.len() +} // Ici, s sort de la portée. Mais comme elle ne prend pas possession de ce + // à quoi elle fait référence, il ne se passe rien. +``` + + + +La portée dans laquelle la variable `s` est en vigueur est la même que toute +portée d'un paramètre de fonction, mais nous ne libérons pas ce sur quoi cette +référence pointe quand elle sort de la portée, car nous n'en prenons pas +possession. Lorsque les fonctions ont des références en paramètres au lieu des +valeurs réelles, nous n'avons pas besoin de retourner les valeurs pour les +rendre, car nous n'en avons jamais pris possession. + + + +Quand nous avons des références dans les paramètres d'une fonction, nous +appelons cela *l'emprunt*. Comme dans la vie réelle, quand un objet appartient +à quelqu'un, vous pouvez le lui emprunter. Et quand vous avez fini, vous devez +le lui rendre. + + + +Donc qu'est-ce qui se passe si nous essayons de modifier quelque chose que nous +empruntons ? Essayez le code dans l'encart 4-6. Attention, spoiler : cela ne +fonctionne pas ! + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +fn main() { + let s = String::from("hello"); + + changer(&s); +} + +fn changer(texte: &String) { + texte.push_str(", world"); +} +``` + + + +Entrée 4-6 : Tentative de modification d'une valeur +empruntée. + + + +Voici l'erreur : + + + +```text +error[E0596]: cannot borrow immutable borrowed content `*texte` as mutable + --> error.rs:8:5 + | +7 | fn changer(texte: &String) { + | ------- use `&mut String` here to make mutable +8 | texte.push_str(", world"); + | ^^^^^ cannot borrow as mutable +``` + + + +Comme les variables sont immuables par défaut, les références le sont aussi. +Nous ne sommes pas autorisés à modifier une chose quand nous avons une référence +vers elle. + + + +### Les références mutables + + + +Nous pouvons résoudre l'erreur du code de l'encart 4-6 avec une petite +modification : + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let mut s = String::from("hello"); + + changer(&mut s); +} + +fn changer(texte: &mut String) { + texte.push_str(", world"); +} +``` + + + +D'abord, nous avons dû préciser que `s` est `mut`. Ensuite, nous avons dû +créer une référence mutable avec `&mut s` et accepter de prendre une référence +mutable avec `texte: &mut String`. + + + +Mais les références mutables ont une grosse contrainte : vous ne pouvez avoir +qu'une seule référence mutable pour chaque donnée dans chaque portée. Le code +suivant va échouer : + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +let mut s = String::from("hello"); + +let r1 = &mut s; +let r2 = &mut s; + +println!("{}, {}", r1, r2); +``` + + + +Voici l'erreur : + +```text +error[E0499]: cannot borrow `s` as mutable more than once at a time + -- > src/main.rs:5:14 + | +4 | let r1 = &mut s; + | ------ first mutable borrow occurs here +5 | let r2 = &mut s; + | ^^^^^^ second mutable borrow occurs here +6 | +7 | println!("{}, {}", r1, r2); + | -- first borrow later used here +``` + + + +Cette contrainte autorise les mutations, mais de manière très contrôlée. C'est +quelque chose que les nouveaux Rustacés ont du mal à surmonter, car la plupart +des langages vous permettent de modifier les données quand vous le voulez. + + + +L'avantage d'avoir cette contrainte est que Rust peut empêcher les accès +concurrents au moment de la compilation. Un *accès concurrent* est une situation +de concurrence qui se produit lorsque ces trois facteurs se combinent : + + + +* Deux pointeurs ou plus accèdent à la même donnée au même moment. +* Au moins un des pointeurs est utilisé pour écrire dans cette donnée. +* On n'utilise aucun mécanisme pour synchroniser l'accès aux données. + + + +L'accès concurrent provoque des comportements indéfinis et rend difficile le +diagnostic et la résolution de problèmes lorsque vous essayez de les reproduire +au moment de l'exécution ; Rust évite ce problème parce qu'il ne va pas compiler +du code avec des accès concurrents ! + + + +Comme d'habitude, nous pouvons utiliser des accolades pour créer une nouvelle +portée, pour nous permettre d'avoir plusieurs références mutables, mais pas +*en même temps* : + + + +```rust +let mut s = String::from("hello"); + +{ + let r1 = &mut s; + +} // r1 sort de la portée ici, donc nous pouvons créer une nouvelle référence + // sans problèmes. + +let r2 = &mut s; +``` + + + +Une règle similaire existe pour combiner les références immuables et mutables. +Ce code va mener à une erreur : + + + +```rust,ignore,does_not_compile +let mut s = String::from("hello"); + +let r1 = &s; // sans problème +let r2 = &s; // sans problème +let r3 = &mut s; // GROS PROBLEME + +println!("{}, {}, and {}", r1, r2, r3); +``` + + + +Voici l'erreur : + + + +```text +error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable + -- > src/main.rs:6:14 + | +4 | let r1 = &s; // sans problème + | -- immutable borrow occurs here +5 | let r2 = &s; // sans problème +6 | let r3 = &mut s; // GROS PROBLEME + | ^^^^^^ mutable borrow occurs here +7 | +8 | println!("{}, {}, and {}", r1, r2, r3); + | -- immutable borrow later used here +``` + + + +Ouah ! Nous ne pouvons pas *non plus* avoir une référence mutable pendant que +nous en avons une autre immuable. Les utilisateurs d'une référence immuable ne +s'attendent pas à ce que sa valeur change soudainement ! Cependant, +l'utilisation de plusieurs références immuables ne pose pas de problème, car +simplement lire une donnée ne va pas affecter la lecture de la donnée par les +autres. + + + +Notez bien que la portée d'une référence commence dès qu'elle est introduite et +se poursuit jusqu'au dernier endroit où cette référence est utilisée. Par +exemple, le code suivant va se compiler car la dernière utilisation de la +référence immuable est située avant l'introduction de la référence mutable : + + + + + +```rust,edition2018,ignore +let mut s = String::from("hello"); + +let r1 = &s; // sans problème +let r2 = &s; // sans problème +println!("{} et {}", r1, r2); +// r1 et r2 ne sont plus utilisés à partir d'ici + +let r3 = &mut s; // sans problème +println!("{}", r3); +``` + + + +Les portées des références immuables `r1` et `r2` se terminent après le +`println!` où elles sont utilisées pour la dernière fois, c'est-à-dire avant que +la référence mutable `r3` soit créée. Ces portées ne se chevauchent pas, donc ce +code est autorisé. + + + +Même si ces erreurs d'emprunt peuvent parfois être frustrantes, n'oubliez pas +que le compilateur de Rust nous signale un bogue potentiel en avance (au moment +de la compilation plutôt que l'exécution) et vous montre où se situe exactement +le problème. Ainsi, vous n'avez pas à chercher pourquoi vos données ne +correspondent pas à ce que vous pensiez qu'elles devraient être. + + + +### Les références pendouillantes + + + +Avec les langages qui utilisent les pointeurs, il est facile de créer par erreur +un *pointeur pendouillant* (*dangling pointer*), qui est un pointeur qui pointe +vers un emplacement mémoire qui a été donné à quelqu'un d'autre, en libérant de +la mémoire tout en conservant un pointeur vers cette mémoire. En revanche, avec +Rust, le compilateur garantit que les références ne seront jamais des références +pendouillantes : si nous avons une référence vers une donnée, le compilateur va +s'assurer que cette donnée ne va pas sortir de la portée avant que la référence +vers cette donnée en soit elle-même sortie. + + + +Essayons de créer une référence pendouillante, ce que Rust va empêcher avec une +erreur au moment de la compilation : + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +fn main() { + let reference_vers_rien = pendouille(); +} + +fn pendouille() -> &String { + let s = String::from("hello"); + + &s +} +``` + + + +Voici l'erreur : + + + +```text +error[E0106]: missing lifetime specifier + --> main.rs:5:16 + | +5 | fn pendouille() -> &String { + | ^ expected lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is + no value for it to be borrowed from + = help: consider giving it a 'static lifetime +``` + + + +Ce message d'erreur fait référence à une fonctionnalité que nous n'avons pas +encore vue : les *durées de vie*. Nous aborderons les durées de vie dans le +chapitre 10. Mais, si vous mettez de côté les parties qui parlent de durées de +vie, le message explique pourquoi le code pose problème : + + + +```text +this function's return type contains a borrowed value, but there is no value +for it to be borrowed from. +``` + +Ce qui peut se traduire par : + +```text +Le type de retour de cette fonction contient une valeur empruntée, mais il n'y a +plus aucune valeur qui peut être empruntée. +``` + + + +Regardons de plus près ce qui se passe exactement à chaque étape de notre code +de `pendouille` : + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +fn pendouille() -> &String { // pendouille retourne une référence vers une String + + let s = String::from("hello"); // s est une nouvelle String + + &s // nous retournons une référence vers la String, s +} // Ici, s sort de la portée, et est libéré. Sa mémoire disparaît. + // Attention, danger ! +``` + + + +Comme `s` est créé dans `pendouille`, lorsque le code de `pendouille` est +terminé, la variable `s` sera désallouée. Mais nous avons essayé de retourner +une référence vers elle. Cela veut dire que cette référence va pointer vers une +`String` invalide. Ce n'est pas bon ! Rust ne nous laissera pas faire cela. + + + +Ici la solution est de renvoyer la `String` directement : + + + +```rust +fn ne_pendouille_pas() -> String { + let s = String::from("hello"); + + s +} +``` + + + +Cela fonctionne sans problème. La possession est transférée à la valeur de +retour de la fonction, et rien n'est désalloué. + + + +### Les règles de référencement + + + +Récapitulons ce que nous avons vu à propos des références : + + + +* À un instant donné, vous pouvez avoir *soit* une référence mutable, *soit* un + nombre quelconque de références immuables. +* Les références doivent toujours être en vigueur. + + + +Ensuite, nous aborderons un autre type de référence : les *slices*. diff --git a/FRENCH/src/ch04-03-slices.md b/FRENCH/src/ch04-03-slices.md index d8c9f6f1f2..3a93bfeaf2 100644 --- a/FRENCH/src/ch04-03-slices.md +++ b/FRENCH/src/ch04-03-slices.md @@ -2,7 +2,7 @@ ## The Slice Type --> -## Le type de découpage +## Le type slice -Un autre type de données qui ne prend pas possession est le *découpage*. Un -découpage vous permet d'obtenir une référence vers une suite continue d'éléments -d'une collection plutôt que toute la collection. +Un autre type de donnée qui ne prend pas possession est la *slice*. Une slice +vous permet d'obtenir une référence vers une séquence continue d'éléments d'une +collection plutôt que toute la collection. -Encart 4-7 : La fonction `premier_mot` qui retourne un -indice d'octet provenant du paramètre `String` +Encart 4-7 : La fonction `premier_mot` qui retourne +l'indice d'un octet provenant du paramètre `String` Comme nous avons besoin de parcourir la `String` élément par élément et de -vérifier si la valeur est un espace, nous allons convertir notre `String` en un +vérifier si la valeur est une espace, nous convertissons notre `String` en un tableau d'octets en utilisant la méthode `as_bytes` : -Comme la méthode `enumerate` retourne un tuple, ne pouvons utiliser des motifs +Comme la méthode `enumerate` retourne un tuple, nous pouvons utiliser des motifs pour déstructurer ce tuple, comme nous pourrions le faire n'importe où avec Rust. Donc dans la boucle `for`, nous précisons un motif qui indique que nous -définissons `i` pour l'indice à partir du tuple et `&element` pour l'octet dans +définissons `i` pour l'indice au sein du tuple et `&element` pour l'octet dans le tuple. Comme nous obtenons une référence vers l'élément avec `.iter().enumerate()`, nous utilisons `&` dans le motif. @@ -174,8 +173,8 @@ using the byte literal syntax. If we find a space, we return the position. Otherwise, we return the length of the string by using `s.len()`: --> -Avec la boucle `for`, nous recherchons l'octet qui représente l'espace en -utilisant la syntaxe des mots binaires. Si nous trouvons un espace, nous +Au sein de la boucle `for`, nous recherchons l'octet qui représente l'espace en +utilisant la syntaxe de littéral d'octet. Si nous trouvons une espace, nous retournons sa position. Sinon, nous retournons la taille de la chaîne en utilisant `s.len()` : @@ -191,7 +190,7 @@ s.len() --> ```rust,ignore - if item == b' ' { + if element == b' ' { return i; } } @@ -289,12 +288,12 @@ the variable `s` to try to extract the first word out, but this would be a bug because the contents of `s` have changed since we saved `5` in `word`. --> -Ce programme se compile sans aucune erreur et le serait toujours si nous +Ce programme se compile sans aucune erreur et le ferait toujours si nous utilisions `mot` après avoir appelé `s.clear()`. Comme `mot` n'est pas du tout -lié à `s`, `mot` contient toujours la valeur `5`. Nous pourrions -utiliser cette valeur `5` avec la variable `s` pour essayer d'en extraire le -premier mot, mais cela serait un bogue, car le contenu de `s` a changé depuis -que nous avons enregistré `5` dans `mot`. +lié à `s`, `mot` contient toujours la valeur `5`. Nous pourrions utiliser cette +valeur `5` avec la variable `s` pour essayer d'en extraire le premier mot, mais +cela serait un bogue, car le contenu de `s` a changé depuis que nous avons +enregistré `5` dans `mot`. -Maintenant nous avons un indice de début *et* un indice de fin, donc nous avons -encore plus de valeurs qui sont calculées à partir de la donnée à un instant -donné, mais qui n'est pas en temps réel. Nous avons maintenant trois variables -isolées qui ont besoin d'être maintenues à jour. +Maintenant, nous avons un indice de début *et* un indice de fin, donc nous avons +encore plus de valeurs qui sont calculées à partir d'une donnée dans un état +donné, mais qui ne sont pas liées du tout à l'état de cette donnée. Nous avons +maintenant trois variables isolées qui ont besoin d'être maintenues à jour. -Heureusement, Rust a une solution pour ce problème : les découpages de chaînes -de caractères. +Heureusement, Rust a une solution pour ce problème : les *slices* de chaînes de +caractères. -### Les découpages de chaînes de caractères +### Les slices de chaînes de caractères -Un *découpage de chaîne de caractère* est une référence à une partie -d'une `String`, et ressemble à ceci : - - +Une *slice de chaîne de caractères* (ou *slice de chaîne*) est une référence à +une partie d'une `String`, et ressemble à ceci : ```rust let s = String::from("hello world"); @@ -385,14 +375,14 @@ the length of the slice, which corresponds to `ending_index` minus a slice that contains a pointer to the 7th byte of `s` with a length value of 5. --> -Nous pouvons créer des découpages en utilisant un intervalle entre crochets en -spécifiant `[indice_debut..indice_fin]`, où `indice_debut` est la première -position dans le découpage et `indice_fin` est la dernière position dans le -découpage plus une position. En interne, la structure de données du découpage -enregistre la position de départ et la longueur du découpage, ce qui correspond -à `indice_fin` moins `indice_debut`. Donc dans le cas du -`let world = &s[6..11];`, `world` va être un découpage qui a un pointeur vers le -7e octet de `s` et une longueur de 5. +Nous pouvons créer des slices en utilisant un intervalle entre crochets en +spécifiant `[indice_debut..indice_fin]`, où `indice_debut` est la position du +premier octet de la slice et `indice_fin` est la position juste après le dernier +octet de la slice. En interne, la structure de données de la slice stocke la +position de départ et la longueur de la slice, ce qui correspond à `indice_fin` +moins `indice_debut`. Donc dans le cas de `let world = &s[6..11];`, `world` est +une slice qui contient un pointeur vers l'octet d'indice 6 de `s` et une +longueur de 5. + -world contient un pointeur vers le 6ième octet de la String s et une longueur de 5 +world contient un pointeur vers l'octet d'indice 6 de la String s et
+une longueur de 5 -Illustration 4-6 : Un découpage d'une chaîne qui pointe -vers une partie d'une `String` +Illustration 4-6 : Une slice de chaîne qui pointe vers +une partie d'une `String` - -```rust -let s = String::from("hello"); - -let decoupage = &s[0..2]; -let decoupage = &s[..2]; -``` -De la même manière, si votre découpage contient les derniers octets de la -`String`, vous pouvez ne rien mettre à la fin. Cela veut dire que ces deux -ceci revient au même : +De la même manière, si votre slice contient le dernier octet de la `String`, +vous pouvez ne rien mettre à la fin. Cela veut dire que ces deux cas sont +identiques : -Vous pouvez aussi ne mettre aucune limite pour créer un découpage de toute la +Vous pouvez aussi ne mettre aucune limite pour créer une slice de toute la chaîne de caractères. Ces deux cas sont donc identiques : -> Remarque : Les indices de l'intervalle d'un découpage d'une chaîne de -> caractères doivent toujours se trouver dans les zones acceptables de -> séparation des caractères encodés en UTF-8. Si vous essayez de créer un -> découpage d'une chaîne de caractères qui s'arrête au milieu d'un caractère -> encodé sur plusieurs octets, votre programme va se fermer avec une erreur. -> Afin de simplifier l'explication des découpages de chaînes de caractères, nous -> utiliserons uniquement l'ASCII dans cette section; nous verons la gestion de -> l'UTF-8 dans une section du [chapitre 8][strings]. +> Remarque : Les indices de l'intervalle d'une slice de chaîne doivent toujours +> se trouver dans les zones acceptables de séparation des caractères encodés en +> UTF-8. Si vous essayez de créer une slice de chaîne qui s'arrête au milieu +> d'un caractère encodé sur plusieurs octets, votre programme va se fermer avec +> une erreur. Afin de simplifier l'explication des slices de chaînes, nous +> utiliserons uniquement l'ASCII dans cette section ; nous verrons la gestion +> d'UTF-8 dans la section [“Stocker du texte encodé en UTF-8 avec les chaînes de +> caractères”][strings] du chapitre 8. -Maintenant que nous savons tout cela, essayons de ré-écrire `premier_mot` pour -retourner un découpage. Le type pour les “découpages de chaînes de caractères” +Maintenant que nous savons tout cela, essayons de réécrire `premier_mot` pour +qu'il retourne une slice. Le type pour les slices de chaînes de caractères s'écrit `&str` : Nous récupérons l'indice de la fin du mot de la même façon que nous l'avions -fait dans l'encart 4-7, en cherchant la première occurrence d'un espace. Lorsque -nous trouvons un espace, nous retournons un découpage de la chaîne de caractère -en utilisant le début de la chaîne de caractères et l'indice de l'espace comme -indices de début et fin. +fait dans l'encart 4-7, en cherchant la première occurrence d'une espace. +Lorsque nous trouvons une espace, nous retournons une slice de chaîne en +utilisant le début de la chaîne de caractères et l'indice de l'espace comme +indices de début et de fin respectivement. -Désormais quand nous appelons `premier_mot`, nous récupérons une seule valeur -qui est liée à la donnée de base. La valeur est construite avec une référence -vers le point de départ du découpage et avec le nombre d'éléments dans le -découpage. +Désormais, quand nous appelons `premier_mot`, nous récupérons une unique valeur +qui est liée à la donnée de base. La valeur se compose d'une référence vers le +point de départ de la slice et du nombre d'éléments dans la slice. -Retourner un découpage fonctionnerait aussi pour une fonction `second_mot` : +Retourner une slice fonctionnerait aussi pour une fonction `second_mot` : -Nous avons maintenant une API simple qui est bien plus difficile à perturber, +Nous avons maintenant une API simple qui est bien plus difficile à mal utiliser, puisque le compilateur va s'assurer que les références dans la `String` seront -toujours en vigueur. Souvenez-vous du bogue du programme de l'encart 4-8, +toujours en vigueur. Vous souvenez-vous du bogue du programme de l'encart 4-8, lorsque nous avions un indice vers la fin du premier mot mais qu'ensuite nous -avions vidé la chaîne de caractères et que notre index n'était plus valide ? Ce +avions vidé la chaîne de caractères et que notre indice n'était plus valide ? Ce code était logiquement incorrect, mais ne montrait pas immédiatement une erreur. Les problèmes apparaîtront plus tard si nous essayons d'utiliser l'indice du -premier mot avec une chaîne de caractère qui a été vidée. Les découpages rendent -ce bogue impossible et nous signale bien plus tôt que nous avons un problème -avec notre code. Utiliser la version avec le découpage de `premier_mot` va -afficher une erreur au moment de la compilation : +premier mot avec une chaîne de caractères qui a été vidée. Les slices rendent ce +bogue impossible et nous signalent bien plus tôt que nous avons un problème avec +notre code. Utiliser la version avec la slice de `premier_mot` va causer une +erreur de compilation : -Rappellons-nous que d'après les règles d'emprunt, si nous avons une référence +Rappelons-nous que d'après les règles d'emprunt, si nous avons une référence immuable vers quelque chose, nous ne pouvons pas avoir une référence mutable -en même temps. Etant donné que `clear` a besoin de modifier la `String`, il a +en même temps. Étant donné que `clear` a besoin de modifier la `String`, il a besoin d'une référence mutable. Rust interdit cette situation, et la compilation échoue. Non seulement Rust a simplifié l'utilisation de notre API, mais il a aussi éliminé une catégorie entière d'erreurs au moment de la compilation ! @@ -710,22 +693,16 @@ aussi éliminé une catégorie entière d'erreurs au moment de la compilation ! #### String Literals Are Slices --> -#### Les chaînes de caractères pures sont des découpages +#### Les littéraux de chaîne de caractères sont aussi des slices -Rappellez-vous lorsque nous avons appris que les chaînes de caractères pures -étaient enregistrées dans le binaire. Maintenant que nous connaissons les -découpages, nous pouvons désormais comprendre les chaînes de caractères pures. - - +Rappelez-vous lorsque nous avons appris que les littéraux de chaîne de +caractères étaient enregistrés dans le binaire. Maintenant que nous connaissons +les slices, nous pouvons désormais comprendre les littéraux de chaîne. ```rust let s = "Hello, world!"; @@ -737,24 +714,23 @@ the binary. This is also why string literals are immutable; `&str` is an immutable reference. --> -Ici, le type de `s` est un `&str` : c'est un découpage qui pointe vers un -endroit précis du binaire. C'est aussi la raison pour laquelle les chaînes de -caractères pures sont immuables; `&str` est une référence immuable. +Ici, le type de `s` est un `&str` : c'est une slice qui pointe vers un endroit +précis du binaire. C'est aussi la raison pour laquelle les littéraux de chaîne +sont immuables ; `&str` est une référence immuable. -#### Les découpages de chaînes de caractères en paramètres +#### Les slices de chaînes de caractères en paramètres -Savoir que vous pouvez utiliser des découpages de chaînes de caractères pures et -des `String` nous invite à apporter une petite amélioration sur `premier_mot`, -dont voici sa signature : +Savoir que l'on peut utiliser des slices de littéraux et de `String` nous incite +à apporter une petite amélioration à `premier_mot`, dont voici la signature : -Un Rustacé plus expérimenté écrirait plutôt la signature de l'encart 4_9, car +Un Rustacé plus expérimenté écrirait plutôt la signature de l'encart 4-9, car cela nous permet d'utiliser la même fonction sur les `&String` et aussi les `&str` : @@ -792,7 +768,7 @@ a string slice for the type of the `s` parameter --> Encart 4-9 : Amélioration de la fonction `premier_mot` en -utilisant un découpage de chaîne de caractère comme type du paramètre `s` +utilisant une slice de chaîne de caractères comme type du paramètre `s` -Si nous avons un découpage de chaîne de caractères, nous pouvons lui donner -directement. Si nous avons une `String`, nous pouvons envoyer un découpage de -toute la `String`. Concevoir une fonction afin de prendre un découpage de chaîne -de caractères plutôt qu'une référence à une `String` rend notre API plus -générique et plus utile sans perdre aucune fonctionnalité : +Si nous avons une slice de chaîne, nous pouvons la passer en argument +directement. Si nous avons une `String`, nous pouvons envoyer une slice de toute +la `String`. Définir une fonction qui prend une slice de chaîne plutôt qu'une +référence à une `String` rend notre API plus générique et plus utile sans perdre +aucune fonctionnalité : -### Les autres découpages +### Les autres slices -Les découpages de chaînes de caractères, comme vous pouvez l'imaginer, sont -spécifiques aux chaînes de caractères. Mais il existe aussi un type plus -générique. Imaginons ce tableau de données : - - +Les slices de chaînes de caractères, comme vous pouvez l'imaginer, sont +spécifiques aux chaînes de caractères. Mais il existe aussi un type de slice +plus générique. Imaginons ce tableau de données : ```rust let a = [1, 2, 3, 4, 5]; @@ -904,23 +873,15 @@ Just as we might want to refer to a part of a string, we might want to refer to part of an array. We’d do so like this: --> -Comme nous pouvons nous référer à un échantillon d'une chaîne de caractères, +Tout comme nous pouvons nous référer à une partie d'une chaîne de caractères, nous pouvons nous référer à une partie d'un tableau. Nous pouvons le faire comme ceci : - - -```rust -let a = [1, 2, 3, 4, 5]; - -let decoupage = &a[1..3]; -``` -Ce découpage est de type `&[i32]`. Il fonctionne de la même manière que les -découpages de chaînes de caractères, en enregistrant une référence vers le -premier élément et une longueur. Vous réutiliserez ce type de découpage pour -toutes les autres types de collections. Nous aborderons ces collections en -détail quand lorsque nous verrons les vecteurs au chapitre 8. +Cette slice est de type `&[i32]`. Elle fonctionne de la même manière que les +slices de chaînes de caractères, en enregistrant une référence vers le premier +élément et une longueur. Vous utiliserez ce type de slice pour tous les autres +types de collections. Nous aborderons ces collections en détail quand nous +verrons les vecteurs au chapitre 8. -Les concepts de possession, d'emprunt, et les découpages garantissent la -sécurité de la mémoire dans les programmes Rust au moment de la compilation. Le -langage Rust vous donne le contrôle sur l'utilisation de la mémoire comme tous -les autres systèmes de langages de programmation, mais celui qui possède les -données nettoie automatiquement ces données quand il sort de la portée, et cela -vous permet de ne pas avoir à écrire et déboguer du code en plus pour avoir -cette fonctionnalité. +Les concepts de possession, d'emprunt et de slices garantissent la sécurité de +la mémoire dans les programmes Rust au moment de la compilation. Le langage Rust +vous donne le contrôle sur l'utilisation de la mémoire comme tous les autres +langages de programmation système, mais le fait que celui qui possède des +données nettoie automatiquement ces données quand il sort de la portée vous +permet de ne pas avoir à écrire et déboguer du code en plus pour avoir cette +fonctionnalité. + +# Utiliser les structures pour structurer des données apparentées + + + +Une *struct*, ou *structure*, est un type de données personnalisé qui vous +permet de nommer et de rassembler plusieurs valeurs associées qui forment +un groupe cohérent. +Si vous êtes familier avec un langage orienté objet, une structure est en +quelque sorte l'ensemble des attributs d'un objet. +Dans ce chapitre, nous comparerons les tuples avec les structures, nous +montrerons comment utiliser les structures et nous aborderons la définition des +méthodes et des fonctions associées pour spécifier le comportement associé aux +données d'une structure. +Les structures et les énumérations (traitées au chapitre 6) sont les fondements +de la création de nouveaux types au sein de votre programme pour tirer +pleinement parti des vérifications de types effectuées par Rust à la +compilation. diff --git a/FRENCH/src/ch05-01-defining-structs.md b/FRENCH/src/ch05-01-defining-structs.md new file mode 100644 index 0000000000..d4d77b73eb --- /dev/null +++ b/FRENCH/src/ch05-01-defining-structs.md @@ -0,0 +1,719 @@ + + +## Définir et instancier des structures + + + +Les structures sont similaires aux tuples, qu'on a vus au chapitre 3. Comme pour +les tuples, les éléments d'une structure peuvent être de différents types. +Contrairement aux tuples, on doit nommer chaque élément des données afin de +clarifier le rôle de chaque valeur. Grâce à ces noms, les structures sont plus +flexibles que les tuples : on n'a pas à utiliser l'ordre des données pour +spécifier ou accéder aux valeurs d'une instance. + + + +Pour définir une structure, on tape le mot-clé `struct` et on donne un nom à +toute la structure. Le nom d'une structure devrait décrire l'utilisation des +éléments des données regroupés. Ensuite, entre des accolades, on définit le nom +et le type de chaque élément des données, qu'on appelle un *champ*. Par exemple, +l'encart 5-1 montre une structure qui stocke des informations à propos d'un +compte d'utilisateur. + + + +```rust +struct Utilisateur { + pseudo: String, + email: String, + nombre_de_connexions: u64, + actif: bool, +} +``` + + + +Encart 5-1 : La définition d'une structure +`Utilisateur` + + + +Pour utiliser une structure après l'avoir définie, on crée une *instance* de +cette structure en indiquant des valeurs concrètes pour chacun des champs. +On crée une instance en indiquant le nom de la structure puis en ajoutant des +accolades qui contiennent des paires de `clé: valeur`, où les clés sont les noms +des champs et les valeurs sont les données que l'on souhaite stocker dans ces +champs. Nous n'avons pas à préciser les champs dans le même ordre qu'on les a +déclarés dans la structure. En d'autres termes, la définition de la structure +décrit un gabarit pour le type, et les instances remplissent ce gabarit avec des +données précises pour créer des valeurs de ce type. Par exemple, nous pouvons +déclarer un utilisateur précis comme dans l'encart 5-2. + + + +```rust +# struct Utilisateur { +# pseudo: String, +# email: String, +# nombre_de_connexions: u64, +# actif: bool, +# } +# +let utilisateur1 = Utilisateur { + email: String::from("quelquun@example.com"), + pseudo: String::from("pseudoquelconque123"), + actif: true, + nombre_de_connexions: 1, +}; +``` + + + +Encart 5-2 : Création d'une instance de la structure +`Utilisateur` + + + +Pour obtenir une valeur spécifique depuis une structure, on utilise la notation +avec le point. Si nous voulions seulement l'adresse e-mail de cet utilisateur, +on pourrait utiliser `utilisateur1.email` partout où on voudrait utiliser cette +valeur. Si l'instance est mutable, nous pourrions changer une valeur en +utilisant la notation avec le point et assigner une valeur à ce champ en +particulier. L'encart 5-3 montre comment changer la valeur du champ `email` +d'une instance mutable de `Utilisateur`. + + + +```rust +# struct Utilisateur { +# pseudo: String, +# email: String, +# nombre_de_connexions: u64, +# actif: bool, +# } +# +let mut utilisateur1 = Utilisateur { + email: String::from("quelquun@example.com"), + pseudo: String::from("pseudoquelconque123"), + actif: true, + nombre_de_connexions: 1, +}; + +utilisateur1.email = String::from("unautremail@example.com"); +``` + + + +Encart 5-3 : Changement de la valeur du champ `email` +d'une instance de `Utilisateur` + + + +À noter que l'instance tout entière doit être mutable ; Rust ne nous permet pas +de marquer seulement certains champs comme mutables. Comme pour toute +expression, nous pouvons construire une nouvelle instance de la structure comme +dernière expression du corps d'une fonction pour retourner implicitement cette +nouvelle instance. + + + +L'encart 5-4 montre une fonction `creer_utilisateur` qui retourne une instance +de `Utilisateur` avec l'adresse e-mail et le pseudo fournis. Le champ `actif` +prend la valeur `true` et le `nombre_de_connexions` prend la valeur `1`. + + + +```rust +# struct Utilisateur { +# pseudo: String, +# email: String, +# nombre_de_connexions: u64, +# actif: bool, +# } +# +fn creer_utilisateur(email: String, pseudo: String) -> Utilisateur { + Utilisateur { + email: email, + pseudo: pseudo, + actif: true, + nombre_de_connexions: 1, + } +} +``` + + + +Encart 5-4 : Une fonction `creer_utilisateur` qui prend +en entrée une adresse e-mail et un pseudo et retourne une instance de +`Utilisateur` + + + +Il est logique de nommer les paramètres de fonction avec le même nom que les +champs de la structure, mais devoir répéter les noms de variables et de champs +`email` et `pseudo` est un peu pénible. Si la structure avait plus de champs, +répéter chaque nom serait encore plus fatigant. Heureusement, il existe un +raccourci pratique ! + + + +### Utiliser le raccourci d'initialisation des champs lorsque les variables et les champs ont le même nom + + + +Puisque les noms des paramètres et les noms de champs de la structure sont +exactement les mêmes dans l'encart 5-4, on peut utiliser la syntaxe de +*raccourci d'initialisation des champs* pour réécrire `creer_utilisateur` de +sorte qu'elle se comporte exactement de la même façon sans avoir à répéter +`email` et `pseudo`, comme le montre l'encart 5-5. + + + +```rust +# struct Utilisateur { +# pseudo: String, +# email: String, +# nombre_de_connexions: u64, +# actif: bool, +# } +# +fn creer_utilisateur(email: String, pseudo: String) -> Utilisateur { + Utilisateur { + email, + pseudo, + actif: true, + nombre_de_connexions: 1, + } +} +``` + + + +Encart 5-5 : Une fonction `creer_utilisateur` qui utilise +le raccourci d'initialisation des champs parce que les paramètres `email` et +`pseudo` ont le même nom que les champs de la structure + + + +Ici, on crée une nouvelle instance de la structure `Utilisateur`, qui possède +un champ nommé `email`. On veut donner au champ `email` la valeur du paramètre +`email` de la fonction `creer_utilisateur`. Comme le champ `email` et le +paramètre `email` ont le même nom, on a uniquement besoin d'écrire `email` +plutôt que `email: email`. + + + +### Créer des instances à partir d'autres instances avec la syntaxe de mise à jour de structure + + + +Il est souvent utile de créer une nouvelle instance de structure qui utilise la +plupart des valeurs d'une ancienne instance tout en en changeant certaines. On +utilisera pour cela la *syntaxe de mise à jour de structure*. + + + +Tout d'abord, l'encart 5-6 nous montre comment créer une nouvelle instance de +`Utilisateur` dans `utilisateur2` sans la syntaxe de mise à jour de structure. +On donne de nouvelles valeurs à `email` et `pseudo` mais on utilise pour les +autres champs les mêmes valeurs que dans `utilisateur1` qu'on a créé à +l'encart 5-2. + + + +```rust +# struct Utilisateur { +# pseudo: String, +# email: String, +# nombre_de_connexions: u64, +# actif: bool, +# } +# +# let utilisateur1 = Utilisateur { +# email: String::from("quelquun@example.com"), +# pseudo: String::from("pseudoquelconque123"), +# actif: true, +# nombre_de_connexions: 1, +# }; +# +let utilisateur2 = Utilisateur { + email: String::from("quelquundautre@example.com"), + pseudo: String::from("autrepseudo567"), + actif: utilisateur1.actif, + nombre_de_connexions: utilisateur1.nombre_de_connexions, +}; +``` + + + +Encart 5-6 : Création d'une nouvelle instance de +`Utilisateur` en utilisant certaines valeurs de `utilisateur1`. + + + +En utilisant la syntaxe de mise à jour de structure, on peut produire le même +résultat avec moins de code, comme le montre l'encart 5-7. La syntaxe `..` +indique que les autres champs auxquels on ne donne pas explicitement de valeur +devraient avoir la même valeur que dans l'instance précisée. + + + +```rust +# struct Utilisateur { +# pseudo: String, +# email: String, +# nombre_de_connexions: u64, +# actif: bool, +# } +# +# let utilisateur1 = Utilisateur { +# email: String::from("quelquun@example.com"), +# pseudo: String::from("pseudoquelconque123"), +# actif: true, +# nombre_de_connexions: 1, +# }; +# +let utilisateur2 = Utilisateur { + email: String::from("quelquundautre@example.com"), + pseudo: String::from("autrepseudo567"), + ..utilisateur1 +}; +``` + + + +Encart 5-7 : Utilisation de la syntaxe de mise à jour de +structure pour assigner de nouvelles valeurs à `email` et `pseudo` à une +nouvelle instance de `Utilisateur` tout en utilisant les autres valeurs des +champs de l'instance de la variable `utilisateur1` + + + +Le code dans l'encart 5-7 crée aussi une instance dans `utilisateur2` qui a une +valeur différente pour `email` et `pseudo` mais qui a les mêmes valeurs pour les +champs `actif` et `nombre_de_connexions` que `utilisateur1`. + + + +### Utilisation de structures tuples sans champ nommé pour créer des types différents + + + +On peut aussi définir des structures qui ressemblent à des tuples, appelées +*structures tuples*. La signification d'une structure tuple est donnée par son +nom. En revanche, ses champs ne sont pas nommés ; on ne précise que leurs types. +Les structures tuples servent lorsqu'on veut donner un nom à un tuple pour qu'il +ait un type différent des autres tuples, mais que nommer chaque champ comme dans +une structure classique serait trop verbeux ou redondant. + + + +La définition d'une structure tuple commence par le mot-clé `struct` et le nom +de la structure suivis des types des champs du tuple. Par exemple, voici une +définition et une utilisation de deux structures tuples nommées `Couleur` et +`Point` : + + + +```rust +struct Couleur(i32, i32, i32); +struct Point(i32, i32, i32); + +let noir = Couleur(0, 0, 0); +let origine = Point(0, 0, 0); +``` + + + +Notez que les valeurs `noir` et `origine` sont de types différents parce que ce +sont des instances de structures tuples différentes. Chaque structure que l'on +définit constitue son propre type, même si les champs au sein de la structure +ont les mêmes types. Par exemple, une fonction qui prend un paramètre de type +`Couleur` ne peut pas prendre un argument de type `Point` à la place, bien que +ces deux types soient tous les deux constitués de trois valeurs `i32`. Mis à +part cela, les instances de stuctures tuples se comportent comme des tuples : on +peut les déstructurer en éléments individuels, on peut utiliser un `.` suivi de +l'indice pour accéder individuellement à une valeur, et ainsi de suite. + + + +### Les structures unité sans champs + + + +On peut aussi définir des structures qui n'ont pas de champs ! Cela s'appelle +des *structures unité* parce qu'elles se comportent d'une façon analogue au type +unité, `()`. Les structures unité sont utiles lorsqu'on doit implémenter un +trait sur un type mais qu'on n'a aucune donnée à stocker dans le type en +lui-même. Nous aborderons les traits au chapitre 10. + + + +> ### La possession des données d'une structure +> +> Dans la définition de la structure `Utilisateur` de l'encart 5-1, nous avions +> utilisé le type possédé `String` plutôt que le type de *slice* de chaîne de +> caractères `&str`. Il s'agit d'un choix délibéré puisque nous voulons que les +> instances de cette structure possèdent toutes leurs données et que ces données +> restent valides tant que la structure tout entière est valide. +> +> Il est possible pour les structures de stocker des références vers des données +> possédées par autre chose, mais cela nécessiterait d'utiliser des +> *durées de vie*, une fonctionnalité de Rust que nous aborderons au +> chapitre 10. Les durées de vie assurent que les données référencées par une +> structure restent valides tant que la structure l'est aussi. Disons que vous +> essayiez de stocker une référence dans une structure sans indiquer de durées +> de vie, comme ceci, ce qui ne fonctionnera pas : +> +> Fichier : src/main.rs +> +> ```rust,ignore,does_not_compile +> struct Utilisateur { +> pseudo: &str, +> email: &str, +> nombre_de_connexions: u64, +> actif: bool, +> } +> +> fn main() { +> let utilisateur1 = Utilisateur { +> email: "quelquun@example.com", +> pseudo: "pseudoquelconque123", +> actif: true, +> nombre_de_connexions: 1, +> }; +> } +> ``` +> +> Le compilateur réclamera l'ajout des durées de vie : +> +> ```text +> error[E0106]: missing lifetime specifier +> --> +> | +> 2 | username: &str, +> | ^ expected lifetime parameter +> +> error[E0106]: missing lifetime specifier +> --> +> | +> 3 | email: &str, +> | ^ expected lifetime parameter +> ``` +> +> Au chapitre 10, nous aborderons la façon de corriger ces erreurs pour qu'on +> puisse stocker des références dans des structures, mais pour le moment, nous +> résoudrons les erreurs comme celles-ci en utilisant des types possédés comme +> `String` plutôt que des références comme `&str`. diff --git a/FRENCH/src/ch05-02-example-structs.md b/FRENCH/src/ch05-02-example-structs.md new file mode 100644 index 0000000000..d7d57c49b3 --- /dev/null +++ b/FRENCH/src/ch05-02-example-structs.md @@ -0,0 +1,629 @@ + + +## Un exemple de programme qui utilise des structures + + + +Pour comprendre dans quels cas nous voudrions utiliser des structures, écrivons +un programme qui calcule l'aire d'un rectangle. Nous commencerons avec de +simples variables, puis on remaniera le code jusqu'à utiliser des structures à +la place. + + + +Créons un nouveau projet binaire avec Cargo nommé *rectangles* qui prendra la +largeur et la hauteur en pixels d'un rectangle et qui calculera l'aire de ce +rectangle. L'encart 5-8 montre un petit programme qui effectue cette tâche d'une +certaine manière dans le *src/main.rs* de notre projet. + + + +Fichier: src/main.rs + + + +```rust +fn main() { + let largeur1 = 30; + let hauteur1 = 50; + + println!( + "L'aire du rectangle est de {} pixels carrés.", + aire(largeur1, hauteur1) + ); +} + +fn aire(largeur: u32, hauteur: u32) -> u32 { + largeur * hauteur +} +``` + + + +Encart 5-8 : Calcul de l'aire d'un rectangle défini par +les variables distinctes `largeur` et `hauteur` + + + +Maintenant, lancez ce programme avec `cargo run` : + + + +```text +L'aire du rectangle est de 1500 pixels carrés. +``` + + + +Bien que l'encart 5-8 fonctionne et détermine l'aire du rectangle en appelant +la fonction `aire` avec chaque dimension, on peut faire mieux. La largeur et la +hauteur sont couplées entre elles car elles décrivent toutes les deux un rectangle. + + + +Le problème de ce code se voit dans la signature de `aire` : + + + +```rust,ignore +fn aire(largeur: u32, hauteur: u32) -> u32 { +``` + + + +La fonction `aire` est censée calculer l'aire d'un rectangle, mais la fonction +que nous avons écrite a deux paramètres. Les paramètres sont liés, mais ce n'est +exprimé nulle part dans notre programme. Il serait plus lisible et plus gérable +de regrouper ensemble la largeur et la hauteur. Nous avons déjà vu dans la +section [“Le type *tuple*”][the-tuple-type] du chapitre 3 une +façon qui nous permettrait de le faire : en utilisant des tuples. + + + +### Remanier le code avec des tuples + + + +L'encart 5-9 nous montre une autre version de notre programme qui utilise des +tuples. + + + +Fichier : src/main.rs + + + +```rust +fn main() { + let rect1 = (30, 50); + + println!( + "L'aire du rectangle est de {} pixels carrés.", + aire(rect1) + ); +} + +fn aire(dimensions: (u32, u32)) -> u32 { + dimensions.0 * dimensions.1 +} +``` + + + +Encart 5-9 : Renseigner la largeur et la hauteur du +rectangle dans un tuple + + + +D'une certaine façon, ce programme est meilleur. Les tuples nous permettent de +structurer un peu plus et nous ne passons plus qu'un argument. Mais d'une autre +façon, cette version est moins claire : les tuples ne donnent pas de noms à +leurs éléments, donc notre calcul est devenu plus déroutant puisqu'il faut +accéder aux éléments du tuple via leur indice. + + + +Ce n'est pas grave de confondre la largeur et la hauteur pour calculer l'aire, +mais si on voulait afficher le rectangle à l'écran, cela serait problématique ! +Il nous faut garder à l'esprit que la `largeur` est l'élément à l'indice 0 du +tuple et que la `hauteur` est l'élément à l'indice 1. Si quelqu'un d'autre +travaillait sur ce code, il devrait le déduire et s'en souvenir aussi. Il est +facile d'oublier ou de confondre ces valeurs et par conséquent provoquer des +erreurs, parce qu'on n'a pas exprimé la signification de nos données dans notre +code. + + + +### Remanier avec des structures : donner plus de sens + + + +On utilise des structures pour rendre les données plus expressives en leur +donnant des noms. On peut transformer le tuple que nous avons utilisé en un type +de donnée nommé dont ses éléments sont aussi nommés, comme le montre l'encart +5-10. + + + +Fichier : src/main.rs + + + +```rust +struct Rectangle { + largeur: u32, + hauteur: u32, +} + +fn main() { + let rect1 = Rectangle { largeur: 30, hauteur: 50 }; + + println!( + "L'aire du rectangle est de {} pixels carrés.", + aire(&rect1) + ); +} + +fn aire(rectangle: &Rectangle) -> u32 { + rectangle.largeur * rectangle.hauteur +} +``` + + + +Encart 5-10 : Définition d'une structure +`Rectangle` + + + +Ici, on a défini une structure et on l'a appelée `Rectangle`. Entre les +accolades, on a défini les champs `largeur` et `hauteur`, tous deux du type +`u32`. Puis dans `main`, on crée une instance de `Rectangle` de largeur 30 et de +hauteur 50. + + + +Notre fonction `aire` est désormais définie avec un unique paramètre, nommé +`rectangle`, et dont le type est une référence immuable vers une instance de la +structure `Rectangle`. Comme mentionné au chapitre 4, on préfère emprunter la +structure au lieu d'en prendre possession. Ainsi, elle reste en possession de +`main` qui peut continuer à utiliser `rect1` ; c'est pourquoi on utilise le `&` +dans la signature de la fonction ainsi que dans l'appel de fonction. + + + +La fonction `aire` accède aux champs `largeur` et `hauteur` de l'instance de +`Rectangle`. Notre signature de fonction pour `aire` est enfin explicite : +calculer l'aire d'un `Rectangle` en utilisant ses champs `largeur` et `hauteur`. +Cela explique que la largeur et la hauteur sont liées entre elles, et cela donne +des noms descriptifs aux valeurs plutôt que d'utiliser les valeurs du tuple avec +les indices `0` et `1`. On gagne en clarté. + + + +### Ajouter des fonctionnalités utiles avec les traits dérivés + + + +Cela serait bien de pouvoir afficher une instance de `Rectangle` pendant qu'on +débogue notre programme et de voir la valeur de chacun de ses champs. L'encart +5-11 essaye de le faire en utilisant la macro `println!` comme on l'a fait +dans les chapitres précédents. Cependant, cela ne fonctionne pas. + + + +Fichier : src/main.rs + + + +```rust,ignore,does_not_compile +struct Rectangle { + largeur: u32, + hauteur: u32, +} + +fn main() { + let rect1 = Rectangle { largeur: 30, hauteur: 50 }; + + println!("rect1 est {}", rect1); +} +``` + + + +Encart 5-11 : Tentative d'afficher une instance de +`Rectangle` + + + +Lorsqu'on exécute ce code, on obtient ce message d'erreur qui nous informe que +`Rectangle` n'implémente pas le trait `std::fmt::Display` : + +```text +error[E0277]: `Rectangle` doesn't implement `std::fmt::Display` +``` + + + +La macro `println!` peut faire toutes sortes de formatages textuels, et par +défaut, les accolades demandent à `println!` d'utiliser le formatage appelé +`Display`, pour convertir en texte destiné à être vu par l'utilisateur final. +Les types primitifs qu'on a vus jusqu'ici implémentent `Display` par défaut +puisqu'il n'existe qu'une seule façon d'afficher un `1` ou tout autre type +primitif à l'utilisateur. Mais pour les structures, la façon dont `println!` +devrait formater son résultat est moins claire car il y a plus de possibilités +d'affichage : Voulez-vous des virgules ? Voulez-vous afficher les accolades ? +Est-ce que tous les champs devraient être affichés ? À cause de ces ambiguïtés, +Rust n'essaye pas de deviner ce qu'on veut, et les structures n'implémentent pas +`Display` par défaut. + + + +Si nous continuons de lire les erreurs, nous trouvons cette remarque utile : + +```text += help: the trait `std::fmt::Display` is not implemented for `Rectangle` += note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +``` + +Le compilateur nous informe que dans notre chaîne de formatage, on est peut-être +en mesure d'utiliser `{:?}` (ou `{:#?}` pour un affichage plus élégant). + + + +Essayons cela ! L'appel de la macro `println!` ressemble maintenant à +`println!("rect1 est {:?}", rect1);`. Insérer le sélecteur `:?` entre les +accolades permet d'indiquer à `println!` que nous voulons utiliser le formatage +appelé `Debug`. Le trait `Debug` nous permet d'afficher notre structure d'une +manière utile aux développeurs pour qu'on puisse voir sa valeur pendant qu'on +débogue le code. + + + +Exécutez le code avec ce changement. Zut ! On a encore une erreur, nous +informant cette fois-ci que `Rectangle` n'implémente pas `std::fmt::Debug` : + +```text +error[E0277]: `Rectangle` doesn't implement `std::fmt::Debug` +``` + + + +Mais une nouvelle fois, le compilateur nous fait une remarque utile : + +```text += help: the trait `std::fmt::Debug` is not implemented for `Rectangle` += note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug` +``` + +Il nous conseille d'ajouter `#[derive(Debug)]` ou d'implémenter manuellement +`std::fmt::Debug`. + + + +Rust *inclut* bel et bien une fonctionnalité pour afficher des informations de +débogage, mais nous devons l'activer explicitement pour la rendre disponible sur +notre structure. Pour ce faire, on ajoute l'annotation `#[derive(Debug)]` juste +avant la définition de la structure, comme le montre l'encart 5-12. + + + +Fichier : src/main.rs + + + +```rust +#[derive(Debug)] +struct Rectangle { + largeur: u32, + hauteur: u32, +} + +fn main() { + let rect1 = Rectangle { largeur: 30, hauteur: 50 }; + + println!("rect1 est {:?}", rect1); +} +``` + + + +Encart 5-12 : L'ajout de l'annotation pour dériver le +trait `Debug` et afficher l'instance de `Rectangle` en utilisant le formatage +de débogage + + + +Maintenant, quand on exécute le programme, nous n'avons plus d'erreurs et ce +texte s'affiche à l'écran : + + + +```text +rect1 est Rectangle { largeur: 30, hauteur: 50 } +``` + + + +Super ! Ce n'est pas le plus beau des affichages, mais cela montre les +valeurs de tous les champs de cette instance, ce qui serait assurément utile +lors du débogage. Quand on a des structures plus grandes, il serait bien d'avoir +un affichage un peu plus lisible ; dans ces cas-là, on pourra utiliser `{:#?}` +au lieu de `{:?}` dans la chaîne de formatage. Quand on utilise `{:#?}` dans cet +exemple, l'affichage donnera plutôt ceci : + + + +```text +rect1 est Rectangle { + largeur: 30, + hauteur: 50, +} +``` + + + +Rust nous fournit un certain nombre de traits qu'on peut utiliser avec +l'annotation `derive` qui peuvent ajouter des comportements utiles à nos propres +types. Ces traits et leurs comportements sont listés à l'annexe C. Nous +expliquerons comment implémenter ces traits avec des comportements personnalisés +et comment créer vos propres traits au chapitre 10. + + + +Notre fonction `aire` est très spécifique : elle ne fait que calculer l'aire +d'un rectangle. Il serait utile de lier un peu plus ce comportement à notre +structure `Rectangle`, puisque cela ne fonctionnera pas avec un autre type. +Voyons comment on peut continuer de remanier ce code en transformant la fonction +`aire` en *méthode* `aire` définie sur notre type `Rectangle`. + + + +[the-tuple-type]: ch03-02-data-types.html#le-type-tuple diff --git a/FRENCH/src/img/trpl04-01.svg b/FRENCH/src/img/trpl04-01.svg new file mode 100644 index 0000000000..c47e21dfc3 --- /dev/null +++ b/FRENCH/src/img/trpl04-01.svg @@ -0,0 +1,68 @@ + + + + + + +%3 + + + +table0 + +s1 + +nom + +valeur + +pointeur + + +taille + +5 + +capacité + +5 + + + +table1 + +indice + +valeur + +0 + +h + +1 + +e + +2 + +l + +3 + +l + +4 + +o + + + +table0:c->table1:pointee + + + + + diff --git a/FRENCH/src/img/trpl04-02.svg b/FRENCH/src/img/trpl04-02.svg new file mode 100644 index 0000000000..f336171be4 --- /dev/null +++ b/FRENCH/src/img/trpl04-02.svg @@ -0,0 +1,95 @@ + + + + + + +%3 + + + +table0 + +s1 + +nom + +valeur + +pointeur + + +taille + +5 + +capacité + +5 + + + +table1 + +indice + +valeur + +0 + +h + +1 + +e + +2 + +l + +3 + +l + +4 + +o + + + +table0:c->table1:pointee + + + + + +table3 + +s2 + +nom + +valeur + +pointeur + + +taille + +5 + +capacité + +5 + + + +table3:c->table1:pointee + + + + + diff --git a/FRENCH/src/img/trpl04-03.svg b/FRENCH/src/img/trpl04-03.svg new file mode 100644 index 0000000000..29bea36bef --- /dev/null +++ b/FRENCH/src/img/trpl04-03.svg @@ -0,0 +1,123 @@ + + + + + + +%3 + + + +table0 + +s2 + +nom + +valeur + +pointeur + + +taille + +5 + +capacité + +5 + + + +table1 + +indice + +valeur + +0 + +h + +1 + +e + +2 + +l + +3 + +l + +4 + +o + + + +table0:c->table1:pointee + + + + + +table3 + +s1 + +nom + +valeur + +pointeur + + +taille + +5 + +capacité + +5 + + + +table4 + +indice + +valeur + +0 + +h + +1 + +e + +2 + +l + +3 + +l + +4 + +o + + + +table3:c->table4:pointee + + + + + diff --git a/FRENCH/src/img/trpl04-04.svg b/FRENCH/src/img/trpl04-04.svg new file mode 100644 index 0000000000..893f22404a --- /dev/null +++ b/FRENCH/src/img/trpl04-04.svg @@ -0,0 +1,96 @@ + + + + + + +%3 + + + +table0 + + +s1 + +nom + +valeur + +pointeur + + +taille + +5 + +capacité + +5 + + + +table1 + +indice + +valeur + +0 + +h + +1 + +e + +2 + +l + +3 + +l + +4 + +o + + + +table0:c->table1:pointee + + + + + +table3 + +s2 + +nom + +valeur + +pointeur + + +taille + +5 + +capacité + +5 + + + +table3:c->table1:pointee + + + + + diff --git a/FRENCH/src/img/trpl04-05.svg b/FRENCH/src/img/trpl04-05.svg new file mode 100644 index 0000000000..411c926f22 --- /dev/null +++ b/FRENCH/src/img/trpl04-05.svg @@ -0,0 +1,87 @@ + + + + + + +%3 + + + +table0 + +s + +nom + +valeur + +pointeur + + + + +table1 + +s1 + +nom + +valeur + +pointeur + + +taille + +5 + +capacité + +5 + + + +table0:c->table1:borrowee + + + + + +table2 + +indice + +valeur + +0 + +h + +1 + +e + +2 + +l + +3 + +l + +4 + +o + + + +table1:c->table2:pointee + + + + + diff --git a/FRENCH/src/img/trpl04-06.svg b/FRENCH/src/img/trpl04-06.svg index e64415fe43..20ab84ea93 100644 --- a/FRENCH/src/img/trpl04-06.svg +++ b/FRENCH/src/img/trpl04-06.svg @@ -5,111 +5,111 @@ --> - + viewBox="0.00 0.00 1000.00 1200.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + %3 - + table0 - -world - -name - -value - -ptr - - -len - -5 + +world + +nom + +valeur + +pointeur + + +taille + +5 table4 - -index - -value - -0 - -h - -1 - -e - -2 - -l - -3 - -l - -4 - -o - -5 - - - -6 - -w - -7 - -o - -8 - -r - -9 - -l - -10 - -d + +indice + +valeur + +0 + +h + +1 + +e + +2 + +l + +3 + +l + +4 + +o + +5 + + + +6 + +w + +7 + +o + +8 + +r + +9 + +l + +10 + +d table0:c->table4:pointee2 - - + + table3 - -s - -name - -value - -ptr - - -len - -11 - -capacity - -11 + +s + +nom + +valeur + +pointeur + + +taille + +11 + +capacité + +11 table3:c->table4:pointee - - + + diff --git a/FRENCH/src/img/trpl15-01.svg b/FRENCH/src/img/trpl15-01.svg new file mode 100644 index 0000000000..e5ae50baf4 --- /dev/null +++ b/FRENCH/src/img/trpl15-01.svg @@ -0,0 +1,43 @@ + + + + + + +%3 + + + +table0 + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + + + + diff --git a/FRENCH/src/img/trpl15-02.svg b/FRENCH/src/img/trpl15-02.svg new file mode 100644 index 0000000000..c811d858cd --- /dev/null +++ b/FRENCH/src/img/trpl15-02.svg @@ -0,0 +1,26 @@ + + + + + + +%3 + + + +table0 + +Cons + +i32 + + +Box + +usize + + + diff --git a/FRENCH/src/img/trpl15-03.svg b/FRENCH/src/img/trpl15-03.svg new file mode 100644 index 0000000000..7d8b5b1ac9 --- /dev/null +++ b/FRENCH/src/img/trpl15-03.svg @@ -0,0 +1,109 @@ + + + + + + +%3 + + + +table4 +b + + + +table5 + +3 + +   + + + +table4:c->table5:pte4 + + + + + +table1 + +5 + +   + + + +table5:c->table1:pte0 + + + + + +table0 +a + + + +table0:c->table1:pte0 + + + + + +table2 + +10 + +   + + + +table1:c->table2:pte1 + + + + + +table3 + +Nil + + + +table2:c->table3:pte2 + + + + + +table6 +c + + + +table7 + +4 + +   + + + +table6:c->table7:pte6 + + + + + +table7:c->table1:pte0 + + + + + diff --git a/FRENCH/src/img/trpl15-04.svg b/FRENCH/src/img/trpl15-04.svg new file mode 100644 index 0000000000..4a39fc48c8 --- /dev/null +++ b/FRENCH/src/img/trpl15-04.svg @@ -0,0 +1,55 @@ + + + + + + +%3 + + +table0 + +a + + +table1 + +5 + + + + +table2 + +b + + +table3 + +10 + + + + +table0:ref->table1:data + + + + +table1:ref->table2:data + + + + +table2:ref->table3:data + + + + +table3:ref->table0:data + + + + + diff --git a/FRENCH/src/title-page.md b/FRENCH/src/title-page.md index a343fa773c..dad37a1989 100644 --- a/FRENCH/src/title-page.md +++ b/FRENCH/src/title-page.md @@ -12,14 +12,14 @@ Rust* -Cette version du document suppose que vous utilisez Rust 1.31.0 ou ultérieur +Cette version du document suppose que vous utilisez Rust 1.37.0 ou ultérieur avec `edition="2018"` dans *Cargo.toml* de tous les projets afin d'utiliser les expressions idiomatiques de l'édition 2018 de Rust. Voir la [section “Installation” du chapitre 1][install] diff --git a/FRENCH/src/translation-terms.md b/FRENCH/src/translation-terms.md index ed25c7946a..c4fa27b614 100644 --- a/FRENCH/src/translation-terms.md +++ b/FRENCH/src/translation-terms.md @@ -6,15 +6,20 @@ français. | Anglais | Français | Remarques | | ------- | ------ | ------ | | ahead-of-time compilation | compilation anticipée | sigle : AOT | +| allocated | alloué | - | +| annotate | indiquer | - | | Appendix | annexe | tout en minuscule (sauf en début de phrase) | | Application Programming Interface (API) | interface de programmation applicative (API) | - | +| assign | assigner | - | | argument | argument / paramètre | - | | arm | branche | dans une expression `match` | | array | tableau | - | | artifact | artéfact | - | | associated function | fonction associée | - | +| attributes | attributs | - | | binary crate | crate binaire | s'utilise au féminin | | *n*-bit number | nombre encodé sur *n* bits | - | +| blob | blob | - | | boolean | booléen | - | | borrow | emprunt(er) | - | | bug | bogue | - | @@ -25,35 +30,51 @@ français. | Chapter | chapitre | tout en minuscule (sauf en début de phrase) | | closure | fermeture | - | | code review | revue de code | - | +| collection | collection | - | | command | commande | dans un terminal | | commit | commit | - | +| compound | composé | - | | concept chapter | chapitre théorique | - | | concurrency | concurrence | - | | concurrent | concurrent | - | +| conditional | structure conditionnelle | - | | constant | constant / constante | - | +| construct | instruction | - | | control flow construct | structure de contrôle | - | | core of the error | message d'erreur | - | | corruption | corruption / être corrompu | - | +| CPU | processeur | - | | crash | plantage | - | | crate | crate | nom féminin (une *crate*) | | curly bracket | accolade | - | +| dangling | pendouillant | - | +| data race | accès concurrent | - | | data representation | modèle de données | - | +| deallocate | désalloué | - | | debug | déboguer | - | | debugging | débogage | - | +| deep copy | copie en profondeur | - | | dependency | dépendance | - | +| dereferencing | déréférencement | - | | destructure | déstructurer | - | | DevOps | DevOps | - | | dot notation | la notation avec un point | - | +| double free | double libération | - | | enum | énumération | - | | enumeration | énumération | - | | enum’s variant | variante d'énumération | - | | exploit | faille | - | | expression | expression | - | | field | champ | d'une structure | +| Figure | Illustration | - | | flag | drapeau | pour les programmes en ligne de commande | +| float | nombre à virgule flottante | - | | floating-point number | nombre à virgule flottante | - | | function | fonction | - | +| functional programming | programmation fonctionnelle | - | +| garbage collector | ramasse-miettes | - | | generics | génériques / généricité | - | +| global scope | portée globale | - | | guessing game | jeu de devinettes | - | | handle | référence abstraite | - | | hash | hash / relatif au hachage | - | @@ -65,11 +86,15 @@ français. | immutability | immuabilité | - | | immutable | immuable | - | | index | indice | - | +| indexing | indexation | - | | input/output | entrée/sortie | sigle : IO | | instance | instance | - | | instantiate | instancier | créer une instance | +| integer literal | littéral d'entiers | - | | integer overflow | dépassement d'entier | - | | Integrated Development Environment (IDE) | environnement de développement intégré (IDE) | - | +| interrupt signal | signal d'arrêt | - | +| invalidate | neutraliser | - | | IOT | internet des objets (IOT) | - | | iterator | itérateur | - | | just-in-time compilation | compilation à la volée | sigle : JIT | @@ -80,29 +105,42 @@ français. | lifetime | durée de vie | - | | linker | linker | - | | Listing | encart | tout en minuscule (sauf en début de phrase) | +| loop | boucle | - | | low-level | bas niveau | - | | machine learning | apprentissage automatique | - | | macro | macro | - | | main | main | - | | memory management | gestion de mémoire | - | | method | méthode | - | +| modern | récent | - | | module | module | - | +| move | déplacement | - | | mutability | mutabilité | - | | mutable | mutable | modifiable | +| namespace | espace de nom | - | +| namespacing | l'espace de nom | - | +| mutate | muter | - | | Note | remarque | tout en minuscule (sauf en début de phrase) | | numerical characters | chiffres | - | +| object-oriented language | langage orienté objet | - | | operating system | système d'exploitation | - | +| owner | propriétaire | - | | ownership | possession | - | | package manager | système de gestion de paquets | - | | panic | panique(r) | - | | parallelism | parallélisme | - | +| parameter | paramètre | - | | PATH | PATH | - | | pattern | motif | - | | pattern-matching | filtrage par motif | - | | placeholder | espace réservé | `{}` pour `fmt` | +| pointer | pointeur | - | +| popping off the stack | dépiler | - | | prelude | étape préliminaire | - | | procedural macro | macro procédurale | - | | project chapter | chapitre de projet | - | +| race condition | situation de concurrence | - | +| pushing onto the stack | empiler | - | | raw identifier | identificateur brut | - | | README | README | - | | refactoring | remaniement | - | @@ -111,25 +149,33 @@ français. | regression | régression | - | | remainder | modulo | opération `%` | | reproducible build | compilation reproductible | - | +| Resource Acquisition Is Initialization (RAII) | l'acquisition d'une ressource est une initialisation (RAII) | - | | return | retourner | - | | run | exécuter | pour les programmes | | Rustacean | Rustacé | - | +| section header | entête de section | - | +| semantic version | version sémantique | - | +| scalar | scalaire | - | +| scope | portée | - | | script | script | - | | section header | en-tête de section | - | | semantic version | version sémantique | - | | semantic versioning | versionnage sémantique | abréviation : SemVer | | shadow | masquer | remplacer une variable par une autre de même nom | | shadowing | masquage | - | +| shallow copy | copie superficielle | - | | shell | terminal / invite de commande | - | | shorthand | abréviation | - | -| sidebar | barre latérale | - | +| sidebar | volet latéral | - | | signature | signature | d'une fonction | | signed | signé | - | -| `Slice` | `Slice` | - | -| slice | découpage | - | +| slash | barre oblique | - | +| slice | slice | - | | smart pointer | pointeur intelligent | - | -| snip | code inchangé masqué ici | dans un encart | +| snake case | snake case | - | +| snip | partie masquée ici | dans un encart | | space | espace | ce mot est féminin quand on parle du caractère typographique | +| square brackets | crochets | - | | stack | pile | - | | standard | standard *(adj. inv.)* / norme *(n.f.)* | - | | standard error | erreur standard | - | @@ -137,8 +183,9 @@ français. | standard library | bibliothèque standard | - | | standard output | sortie standard | - | | statement | instruction | - | +| statically typed | statiquement typé | - | | string | chaîne de caractères | - | -| string literal | chaîne de caractères pure | - | +| string literal | un littéral de chaîne de caractères | - | | `String` | `String` | nom féminin (une `String`) | | struct | structure | - | | submodule | sous-module | - | @@ -150,13 +197,18 @@ français. | troubleshooting | dépannage | - | | tuple | tuple | - | | tuple struct | structure tuple | - | +| type | type | - | +| type annotation | annotation de type | - | | type inference | inférence de types | - | +| two’s complement | complément à deux | - | +| two’s complement wrapping | rebouclage du complément à deux | - | | underlying operating system | système d'exploitation sous-jacent | - | | underscore | tiret bas | le caractère `_` | -| unit-like struct | structure unitaire | - | +| unit-like struct | structure unité | - | | unit type | type unité | - | | unit value | valeur unité | - | | unsafe | non sécurisé | - | +| unsigned | sans signe (toujours positif) | - | | unsigned | non signé | - | | user input | saisie utilisateur | - | | variable | variable | - | diff --git a/README.md b/README.md index 45e2616402..4999067e43 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ releases are updated less frequently. ## Requirements -Building the book requires [mdBook], ideally the same 0.2.x version that +Building the book requires [mdBook], ideally the same 0.3.x version that rust-lang/rust uses in [this file][rust-mdbook]. To get it: [mdBook]: https://github.com/rust-lang-nursery/mdBook diff --git a/ci/build.sh b/ci/build.sh old mode 100644 new mode 100755 index 9ad1781381..13f4509a5b --- a/ci/build.sh +++ b/ci/build.sh @@ -4,13 +4,6 @@ set -e export PATH=$PATH:/home/travis/.cargo/bin; -# Feature check -cd ci/stable-check - -cargo run -- ../../src - -cd ../.. - echo 'Spellchecking...' bash ci/spellcheck.sh list echo 'Testing...' @@ -19,3 +12,8 @@ echo 'Building...' mdbook build echo 'Linting for local file paths...' cargo run --bin lfp src +echo 'Validating references' +for file in src/*.md ; do + echo Checking references in $file + cargo run --quiet --bin link2print < $file > /dev/null +done diff --git a/ci/stable-check/Cargo.lock b/ci/stable-check/Cargo.lock deleted file mode 100644 index 9a3b307c96..0000000000 --- a/ci/stable-check/Cargo.lock +++ /dev/null @@ -1,4 +0,0 @@ -[root] -name = "stable-check" -version = "0.1.0" - diff --git a/ci/stable-check/Cargo.toml b/ci/stable-check/Cargo.toml deleted file mode 100644 index 691722a0b2..0000000000 --- a/ci/stable-check/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "stable-check" -version = "0.1.0" -authors = ["steveklabnik "] - -[dependencies] diff --git a/ci/stable-check/src/main.rs b/ci/stable-check/src/main.rs deleted file mode 100644 index 167f1f883a..0000000000 --- a/ci/stable-check/src/main.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::error::Error; -use std::env; -use std::fs; -use std::fs::File; -use std::io::prelude::*; -use std::path::Path; - -fn main() { - let arg = env::args().nth(1).unwrap_or_else(|| { - println!("Please pass a src directory as the first argument"); - std::process::exit(1); - }); - - match check_directory(&Path::new(&arg)) { - Ok(()) => println!("passed!"), - Err(e) => { - println!("Error: {}", e); - std::process::exit(1); - } - } - -} - -fn check_directory(dir: &Path) -> Result<(), Box> { - for entry in fs::read_dir(dir)? { - let entry = entry?; - let path = entry.path(); - - if path.is_dir() { - continue; - } - - let mut file = File::open(&path)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - - if contents.contains("#![feature") { - return Err(From::from(format!("Feature flag found in {:?}", path))); - } - } - - Ok(()) -} diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000000..bf50e910e6 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +1.37.0 diff --git a/src/appendix-01-keywords.md b/src/appendix-01-keywords.md index 85e675d8a7..6a129e2903 100644 --- a/src/appendix-01-keywords.md +++ b/src/appendix-01-keywords.md @@ -60,6 +60,7 @@ for potential future use. * `abstract` * `async` +* `await` * `become` * `box` * `do` diff --git a/src/appendix-04-useful-development-tools.md b/src/appendix-04-useful-development-tools.md index ec40d1cbef..33929bc8d0 100644 --- a/src/appendix-04-useful-development-tools.md +++ b/src/appendix-04-useful-development-tools.md @@ -1,10 +1,10 @@ -# Appendix D - Useful Development Tools +## Appendix D - Useful Development Tools In this appendix, we talk about some useful development tools that the Rust project provides. We’ll look at automatic formatting, quick ways to apply warning fixes, a linter, and integrating with IDEs. -## Automatic Formatting with `rustfmt` +### Automatic Formatting with `rustfmt` The `rustfmt` tool reformats your code according to the community code style. Many collaborative projects use `rustfmt` to prevent arguments about which @@ -29,7 +29,7 @@ on `rustfmt`, see [its documentation][rustfmt]. [rustfmt]: https://github.com/rust-lang/rustfmt -## Fix Your Code with `rustfix` +### Fix Your Code with `rustfix` The rustfix tool is included with Rust installations and can automatically fix some compiler warnings. If you’ve written code in Rust, you’ve probably seen @@ -96,7 +96,7 @@ The `for` loop variable is now named `_i`, and the warning no longer appears. You can also use the `cargo fix` command to transition your code between different Rust editions. Editions are covered in Appendix E. -## More Lints with Clippy +### More Lints with Clippy The Clippy tool is a collection of lints to analyze your code so you can catch common mistakes and improve your Rust code. @@ -158,7 +158,7 @@ For more information on Clippy, see [its documentation][clippy]. [clippy]: https://github.com/rust-lang/rust-clippy -## IDE Integration Using the Rust Language Server +### IDE Integration Using the Rust Language Server To help IDE integration, the Rust project distributes the *Rust Language Server* (`rls`). This tool speaks the [Language Server diff --git a/src/appendix-05-editions.md b/src/appendix-05-editions.md index ac75c4181d..db98ecc5ee 100644 --- a/src/appendix-05-editions.md +++ b/src/appendix-05-editions.md @@ -1,4 +1,4 @@ -# Appendix E - Editions +## Appendix E - Editions In Chapter 1, you saw that `cargo new` adds a bit of metadata to your *Cargo.toml* file about an edition. This appendix talks about what that means! diff --git a/src/appendix-07-nightly-rust.md b/src/appendix-07-nightly-rust.md index d8fd0da776..bace82f2e0 100644 --- a/src/appendix-07-nightly-rust.md +++ b/src/appendix-07-nightly-rust.md @@ -1,4 +1,4 @@ -# Appendix G - How Rust is Made and “Nightly Rust” +## Appendix G - How Rust is Made and “Nightly Rust” This appendix is about how Rust is made and how that affects you as a Rust developer. diff --git a/src/ch00-00-introduction.md b/src/ch00-00-introduction.md index 7a37ee4ef4..86fe469f9c 100644 --- a/src/ch00-00-introduction.md +++ b/src/ch00-00-introduction.md @@ -104,7 +104,7 @@ chapters. In concept chapters, you’ll learn about an aspect of Rust. In projec chapters, we’ll build small programs together, applying what you’ve learned so far. Chapters 2, 12, and 20 are project chapters; the rest are concept chapters. -Chapter 1 explains how to install Rust, how to write a Hello, world! program, +Chapter 1 explains how to install Rust, how to write a “Hello, world!” program, and how to use Cargo, Rust’s package manager and build tool. Chapter 2 is a hands-on introduction to the Rust language. Here we cover concepts at a high level, and later chapters will provide additional detail. If you want to get diff --git a/src/ch01-01-installation.md b/src/ch01-01-installation.md index 9061389e6d..d7659ebab8 100644 --- a/src/ch01-01-installation.md +++ b/src/ch01-01-installation.md @@ -126,9 +126,9 @@ resources include [the Users forum][users] and [Stack Overflow][stackoverflow]. ### Local Documentation -The installer also includes a copy of the documentation locally, so you can -read it offline. Run `rustup doc` to open the local documentation in your -browser. +The installation of Rust also includes a copy of the documentation locally, so +you can read it offline. Run `rustup doc` to open the local documentation in +your browser. Any time a type or function is provided by the standard library and you’re not sure what it does or how to use it, use the application programming interface diff --git a/src/ch01-02-hello-world.md b/src/ch01-02-hello-world.md index e82ba49670..82a545ab63 100644 --- a/src/ch01-02-hello-world.md +++ b/src/ch01-02-hello-world.md @@ -20,7 +20,7 @@ we suggest making a *projects* directory in your home directory and keeping all your projects there. Open a terminal and enter the following commands to make a *projects* directory -and a directory for the Hello, world! project within the *projects* directory. +and a directory for the “Hello, world!” project within the *projects* directory. For Linux, macOS, and PowerShell on Windows, enter this: @@ -86,7 +86,7 @@ program. That makes you a Rust programmer—welcome! ### Anatomy of a Rust Program -Let’s review in detail what just happened in your Hello, world! program. +Let’s review in detail what just happened in your “Hello, world!” program. Here’s the first piece of the puzzle: ```rust @@ -178,7 +178,7 @@ From here, you run the *main* or *main.exe* file, like this: $ ./main # or .\main.exe on Windows ``` -If *main.rs* was your Hello, world! program, this line would print `Hello, +If *main.rs* was your “Hello, world!” program, this line would print `Hello, world!` to your terminal. If you’re more familiar with a dynamic language, such as Ruby, Python, or diff --git a/src/ch01-03-hello-cargo.md b/src/ch01-03-hello-cargo.md index 34428e5f94..b40be39da5 100644 --- a/src/ch01-03-hello-cargo.md +++ b/src/ch01-03-hello-cargo.md @@ -6,9 +6,9 @@ such as building your code, downloading the libraries your code depends on, and building those libraries. (We call libraries your code needs *dependencies*.) The simplest Rust programs, like the one we’ve written so far, don’t have any -dependencies. So if we had built the Hello, world! project with Cargo, it would -only use the part of Cargo that handles building your code. As you write more -complex Rust programs, you’ll add dependencies, and if you start a project +dependencies. So if we had built the “Hello, world!” project with Cargo, it +would only use the part of Cargo that handles building your code. As you write +more complex Rust programs, you’ll add dependencies, and if you start a project using Cargo, adding dependencies will be much easier to do. Because the vast majority of Rust projects use Cargo, the rest of this book @@ -29,7 +29,7 @@ determine how to install Cargo separately. ### Creating a Project with Cargo Let’s create a new project using Cargo and look at how it differs from our -original Hello, world! project. Navigate back to your *projects* directory (or +original “Hello, world!” project. Navigate back to your *projects* directory (or wherever you decided to store your code). Then, on any operating system, run the following: @@ -99,10 +99,10 @@ fn main() { } ``` -Cargo has generated a Hello, world! program for you, just like the one we wrote -in Listing 1-1! So far, the differences between our previous project and the -project Cargo generates are that Cargo placed the code in the *src* directory, -and we have a *Cargo.toml* configuration file in the top directory. +Cargo has generated a “Hello, world!” program for you, just like the one we +wrote in Listing 1-1! So far, the differences between our previous project and +the project Cargo generates are that Cargo placed the code in the *src* +directory, and we have a *Cargo.toml* configuration file in the top directory. Cargo expects your source files to live inside the *src* directory. The top-level project directory is just for README files, license information, @@ -110,14 +110,14 @@ configuration files, and anything else not related to your code. Using Cargo helps you organize your projects. There’s a place for everything, and everything is in its place. -If you started a project that doesn’t use Cargo, as we did with the Hello, -world! project, you can convert it to a project that does use Cargo. Move the +If you started a project that doesn’t use Cargo, as we did with the “Hello, +world!” project, you can convert it to a project that does use Cargo. Move the project code into the *src* directory and create an appropriate *Cargo.toml* file. ### Building and Running a Cargo Project -Now let’s look at what’s different when we build and run the Hello, world! +Now let’s look at what’s different when we build and run the “Hello, world!” program with Cargo! From your *hello_cargo* directory, build your project by entering the following command: @@ -237,7 +237,7 @@ you’ve learned how to: * Install the latest stable version of Rust using `rustup` * Update to a newer Rust version * Open locally installed documentation -* Write and run a Hello, world! program using `rustc` directly +* Write and run a “Hello, world!” program using `rustc` directly * Create and run a new project using the conventions of Cargo This is a great time to build a more substantial program to get used to reading diff --git a/src/ch02-00-guessing-game-tutorial.md b/src/ch02-00-guessing-game-tutorial.md index e30316fd58..2ecc1520ea 100644 --- a/src/ch02-00-guessing-game-tutorial.md +++ b/src/ch02-00-guessing-game-tutorial.md @@ -201,7 +201,7 @@ io::stdin().read_line(&mut guess) .expect("Failed to read line"); ``` -If we hadn’t listed the `use std::io` line at the beginning of the program, we +If we hadn’t put the `use std::io` line at the beginning of the program, we could have written this function call as `std::io::stdin`. The `stdin` function returns an instance of [`std::io::Stdin`][iostdin], which is a type that represents a handle to the standard input for your terminal. @@ -373,23 +373,28 @@ code that uses `rand`, we need to modify the *Cargo.toml* file to include the the bottom beneath the `[dependencies]` section header that Cargo created for you: + + Filename: Cargo.toml ```toml [dependencies] - -rand = "0.3.14" +rand = "0.5.5" ``` In the *Cargo.toml* file, everything that follows a header is part of a section that continues until another section starts. The `[dependencies]` section is where you tell Cargo which external crates your project depends on and which versions of those crates you require. In this case, we’ll specify the `rand` -crate with the semantic version specifier `0.3.14`. Cargo understands [Semantic +crate with the semantic version specifier `0.5.5`. Cargo understands [Semantic Versioning][semver] (sometimes called *SemVer*), which is a -standard for writing version numbers. The number `0.3.14` is actually shorthand -for `^0.3.14`, which means “any version that has a public API compatible with -version 0.3.14.” +standard for writing version numbers. The number `0.5.5` is actually shorthand +for `^0.5.5`, which means “any version that has a public API compatible with +version 0.5.5.” [semver]: http://semver.org @@ -398,13 +403,19 @@ Listing 2-2. ```text $ cargo build - Updating registry `https://github.com/rust-lang/crates.io-index` - Downloading rand v0.3.14 - Downloading libc v0.2.14 - Compiling libc v0.2.14 - Compiling rand v0.3.14 + Updating crates.io index + Downloaded rand v0.5.5 + Downloaded libc v0.2.62 + Downloaded rand_core v0.2.2 + Downloaded rand_core v0.3.1 + Downloaded rand_core v0.4.2 + Compiling rand_core v0.4.2 + Compiling libc v0.2.62 + Compiling rand_core v0.3.1 + Compiling rand_core v0.2.2 + Compiling rand v0.5.5 Compiling guessing_game v0.1.0 (file:///projects/guessing_game) - Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs + Finished dev [unoptimized + debuginfo] target(s) in 2.53 s ``` Listing 2-2: The output from running `cargo build` after @@ -422,8 +433,8 @@ their open source Rust projects for others to use. After updating the registry, Cargo checks the `[dependencies]` section and downloads any crates you don’t have yet. In this case, although we only listed -`rand` as a dependency, Cargo also grabbed a copy of `libc`, because `rand` -depends on `libc` to work. After downloading the crates, Rust compiles them and +`rand` as a dependency, Cargo also grabbed `libc` and `rand_core`, because `rand` +depends on those to work. After downloading the crates, Rust compiles them and then compiles the project with the dependencies available. If you immediately run `cargo build` again without making any changes, you @@ -439,7 +450,7 @@ and build again, you’ll only see two lines of output: ```text $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game) - Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs + Finished dev [unoptimized + debuginfo] target(s) in 2.53s ``` These lines show Cargo only updates the build with your tiny change to the @@ -452,7 +463,7 @@ your part of the code. Cargo has a mechanism that ensures you can rebuild the same artifact every time you or anyone else builds your code: Cargo will use only the versions of the dependencies you specified until you indicate otherwise. For example, what -happens if next week version 0.3.15 of the `rand` crate comes out and +happens if next week version 0.5.6 of the `rand` crate comes out and contains an important bug fix but also contains a regression that will break your code? @@ -464,7 +475,7 @@ the *Cargo.lock* file. When you build your project in the future, Cargo will see that the *Cargo.lock* file exists and use the versions specified there rather than doing all the work of figuring out versions again. This lets you have a reproducible build automatically. In other words, your project will -remain at `0.3.14` until you explicitly upgrade, thanks to the *Cargo.lock* +remain at `0.5.5` until you explicitly upgrade, thanks to the *Cargo.lock* file. #### Updating a Crate to Get a New Version @@ -474,26 +485,25 @@ which will ignore the *Cargo.lock* file and figure out all the latest versions that fit your specifications in *Cargo.toml*. If that works, Cargo will write those versions to the *Cargo.lock* file. -But by default, Cargo will only look for versions greater than `0.3.0` and less -than `0.4.0`. If the `rand` crate has released two new versions, `0.3.15` and -`0.4.0`, you would see the following if you ran `cargo update`: +But by default, Cargo will only look for versions greater than `0.5.5` and less +than `0.6.0`. If the `rand` crate has released two new versions, `0.5.6` and +`0.6.0`, you would see the following if you ran `cargo update`: ```text $ cargo update - Updating registry `https://github.com/rust-lang/crates.io-index` - Updating rand v0.3.14 -> v0.3.15 + Updating crates.io index + Updating rand v0.5.5 -> v0.5.6 ``` At this point, you would also notice a change in your *Cargo.lock* file noting -that the version of the `rand` crate you are now using is `0.3.15`. +that the version of the `rand` crate you are now using is `0.5.6`. -If you wanted to use `rand` version `0.4.0` or any version in the `0.4.x` +If you wanted to use `rand` version `0.6.0` or any version in the `0.6.x` series, you’d have to update the *Cargo.toml* file to look like this instead: ```toml [dependencies] - -rand = "0.4.0" +rand = "0.6.0" ``` The next time you run `cargo build`, Cargo will update the registry of crates @@ -668,7 +678,7 @@ error[E0308]: mismatched types --> src/main.rs:23:21 | 23 | match guess.cmp(&secret_number) { - | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable + | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integer | = note: expected type `&std::string::String` = note: found type `&{integer}` diff --git a/src/ch03-01-variables-and-mutability.md b/src/ch03-01-variables-and-mutability.md index 6f5e6c7b15..d6a73a092e 100644 --- a/src/ch03-01-variables-and-mutability.md +++ b/src/ch03-01-variables-and-mutability.md @@ -65,7 +65,7 @@ But mutability can be very useful. Variables are immutable only by default; as you did in Chapter 2, you can make them mutable by adding `mut` in front of the variable name. In addition to allowing this value to change, `mut` conveys intent to future readers of the code by indicating that other parts of the code -will be changing this variable value. +will be changing this variable’s value. For example, let’s change *src/main.rs* to the following: diff --git a/src/ch03-02-data-types.md b/src/ch03-02-data-types.md index 228e4316b2..482e69f73f 100644 --- a/src/ch03-02-data-types.md +++ b/src/ch03-02-data-types.md @@ -228,9 +228,9 @@ primitive compound types: tuples and arrays. #### The Tuple Type -A tuple is a general way of grouping together some number of other values -with a variety of types into one compound type. Tuples have a fixed length: -once declared, they cannot grow or shrink in size. +A tuple is a general way of grouping together a number of values with a variety +of types into one compound type. Tuples have a fixed length: once declared, +they cannot grow or shrink in size. We create a tuple by writing a comma-separated list of values inside parentheses. Each position in the tuple has a type, and the types of the @@ -286,8 +286,8 @@ fn main() { ``` This program creates a tuple, `x`, and then makes new variables for each -element by using their index. As with most programming languages, the first -index in a tuple is 0. +element by using their respective indices. As with most programming languages, +the first index in a tuple is 0. #### The Array Type @@ -318,7 +318,7 @@ vector. Chapter 8 discusses vectors in more detail. An example of when you might want to use an array rather than a vector is in a program that needs to know the names of the months of the year. It’s very unlikely that such a program will need to add or remove months, so you can use -an array because you know it will always contain 12 items: +an array because you know it will always contain 12 elements: ```rust let months = ["January", "February", "March", "April", "May", "June", "July", @@ -334,7 +334,7 @@ let a: [i32; 5] = [1, 2, 3, 4, 5]; ``` Here, `i32` is the type of each element. After the semicolon, the number `5` -indicates the element contains five items. +indicates the array contains five elements. Writing an array’s type this way looks similar to an alternative syntax for initializing an array: if you want to create an array that contains the same diff --git a/src/ch03-05-control-flow.md b/src/ch03-05-control-flow.md index 52ce3d5a9f..ac529d926d 100644 --- a/src/ch03-05-control-flow.md +++ b/src/ch03-05-control-flow.md @@ -96,7 +96,7 @@ error[E0308]: mismatched types --> src/main.rs:4:8 | 4 | if number { - | ^^^^^^ expected bool, found integral variable + | ^^^^^^ expected bool, found integer | = note: expected type `bool` found type `{integer}` @@ -240,7 +240,7 @@ error[E0308]: if and else have incompatible types 6 | | } else { 7 | | "six" 8 | | }; - | |_____^ expected integral variable, found &str + | |_____^ expected integer, found &str | = note: expected type `{integer}` found type `&str` diff --git a/src/ch04-01-what-is-ownership.md b/src/ch04-01-what-is-ownership.md index 5012d8614b..cf8fd209b4 100644 --- a/src/ch04-01-what-is-ownership.md +++ b/src/ch04-01-what-is-ownership.md @@ -146,9 +146,9 @@ that is stored on the heap and explore how Rust knows when to clean up that data. We’ll use `String` as the example here and concentrate on the parts of `String` -that relate to ownership. These aspects also apply to other complex data types -provided by the standard library and that you create. We’ll discuss `String` in -more depth in Chapter 8. +that relate to ownership. These aspects also apply to other complex data types, +whether they are provided by the standard library or created by you. We’ll +discuss `String` in more depth in Chapter 8. We’ve already seen string literals, where a string value is hardcoded into our program. String literals are convenient, but they aren’t suitable for every diff --git a/src/ch04-02-references-and-borrowing.md b/src/ch04-02-references-and-borrowing.md index 27290fcf04..f75d5c91b8 100644 --- a/src/ch04-02-references-and-borrowing.md +++ b/src/ch04-02-references-and-borrowing.md @@ -314,7 +314,7 @@ Let’s take a closer look at exactly what’s happening at each stage of our Filename: src/main.rs -```rust,ignore +```rust,ignore,does_not_compile fn dangle() -> &String { // dangle returns a reference to a String let s = String::from("hello"); // s is a new String diff --git a/src/ch05-02-example-structs.md b/src/ch05-02-example-structs.md index 4aeb816fe6..46f88f1b8a 100644 --- a/src/ch05-02-example-structs.md +++ b/src/ch05-02-example-structs.md @@ -165,7 +165,7 @@ fn main() { Listing 5-11: Attempting to print a `Rectangle` instance -When we run this code, we get an error with this core message: +When we compile this code, we get an error with this core message: ```text error[E0277]: `Rectangle` doesn't implement `std::fmt::Display` @@ -195,7 +195,7 @@ Let’s try it! The `println!` macro call will now look like `println!("rect1 is enables us to print our struct in a way that is useful for developers so we can see its value while we’re debugging our code. -Run the code with this change. Drat! We still get an error: +Compile the code with this change. Drat! We still get an error: ```text error[E0277]: `Rectangle` doesn't implement `std::fmt::Debug` diff --git a/src/ch06-00-enums.md b/src/ch06-00-enums.md index 767f8668da..cf7ea67f60 100644 --- a/src/ch06-00-enums.md +++ b/src/ch06-00-enums.md @@ -1,7 +1,7 @@ # Enums and Pattern Matching In this chapter we’ll look at *enumerations*, also referred to as *enums*. -Enums allow you to define a type by enumerating its possible values. First, +Enums allow you to define a type by enumerating its possible *variants*. First, we’ll define and use an enum to show how an enum can encode meaning along with data. Next, we’ll explore a particularly useful enum, called `Option`, which expresses that a value can be either something or nothing. Then we’ll look at diff --git a/src/ch06-01-defining-an-enum.md b/src/ch06-01-defining-an-enum.md index 9a56af7372..0d25afbb68 100644 --- a/src/ch06-01-defining-an-enum.md +++ b/src/ch06-01-defining-an-enum.md @@ -5,18 +5,18 @@ are useful and more appropriate than structs in this case. Say we need to work with IP addresses. Currently, two major standards are used for IP addresses: version four and version six. These are the only possibilities for an IP address that our program will come across: we can *enumerate* all possible -values, which is where enumeration gets its name. +variants, which is where enumeration gets its name. Any IP address can be either a version four or a version six address, but not both at the same time. That property of IP addresses makes the enum data -structure appropriate, because enum values can only be one of the variants. +structure appropriate, because enum values can only be one of its variants. Both version four and version six addresses are still fundamentally IP addresses, so they should be treated as the same type when the code is handling situations that apply to any kind of IP address. We can express this concept in code by defining an `IpAddrKind` enumeration and -listing the possible kinds an IP address can be, `V4` and `V6`. These are known -as the *variants* of the enum: +listing the possible kinds an IP address can be, `V4` and `V6`. These are the +variants of the enum: ```rust enum IpAddrKind { diff --git a/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md b/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md index af15ace8b4..05af46cbdc 100644 --- a/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md +++ b/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md @@ -41,10 +41,9 @@ root, `hosting` is now a valid name in that scope, just as though the `hosting` module had been defined in the crate root. Paths brought into scope with `use` also check privacy, like any other paths. -Specifying a relative path with `use` is slightly different. Instead of -starting from a name in the current scope, we must start the path given to -`use` with the keyword `self`. Listing 7-12 shows how to specify a relative -path to get the same behavior as in Listing 7-11. +You can also bring an item into scope with `use` and a relative path. Listing +7-12 shows how to specify a relative path to get the same behavior as in +Listing 7-11. Filename: src/lib.rs @@ -55,7 +54,7 @@ mod front_of_house { } } -use self::front_of_house::hosting; +use front_of_house::hosting; pub fn eat_at_restaurant() { hosting::add_to_waitlist(); @@ -66,10 +65,7 @@ pub fn eat_at_restaurant() { ``` Listing 7-12: Bringing a module into scope with `use` and -a relative path starting with `self` - -Note that using `self` in this way might not be necessary in the future; it’s -an inconsistency in the language that Rust developers are working to eliminate. +a relative path ### Creating Idiomatic `use` Paths @@ -245,6 +241,12 @@ In Chapter 2, we programmed a guessing game project that used an external package called `rand` to get random numbers. To use `rand` in our project, we added this line to *Cargo.toml*: + + Filename: Cargo.toml ```toml diff --git a/src/ch07-05-separating-modules-into-different-files.md b/src/ch07-05-separating-modules-into-different-files.md index 4a039fa83e..6b51859afd 100644 --- a/src/ch07-05-separating-modules-into-different-files.md +++ b/src/ch07-05-separating-modules-into-different-files.md @@ -76,7 +76,7 @@ that module. ## Summary -Rust lets you organize your packages into crates and your crates into modules +Rust lets you split a package into multiple crates and a crate into modules so you can refer to items defined in one module from another module. You can do this by specifying absolute or relative paths. These paths can be brought into scope with a `use` statement so you can use a shorter path for multiple uses of diff --git a/src/ch09-02-recoverable-errors-with-result.md b/src/ch09-02-recoverable-errors-with-result.md index 85c4192836..8461e2bf03 100644 --- a/src/ch09-02-recoverable-errors-with-result.md +++ b/src/ch09-02-recoverable-errors-with-result.md @@ -51,7 +51,7 @@ 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: -```rust,ignore +```rust,ignore,does_not_compile let f: u32 = File::open("hello.txt"); ``` @@ -470,18 +470,18 @@ and returns it. Of course, using `fs::read_to_string` doesn’t give us the opportunity to explain all the error handling, so we did it the longer way first. -#### The `?` Operator Can Only Be Used in Functions That Return `Result` +#### The `?` Operator Can Be Used in Functions That Return `Result` -The `?` operator can only be used in functions that have a return type of +The `?` operator can 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`. +can be a `Result` to be compatible with this `return`. Let’s look at what happens if we use the `?` operator in the `main` function, which you’ll recall has a return type of `()`: -```rust,ignore +```rust,ignore,does_not_compile use std::fs::File; fn main() { @@ -505,8 +505,9 @@ error[E0277]: the `?` operator can only be used in a function that returns ``` This error points out that we’re only allowed to use the `?` operator in a -function that returns `Result`. When you’re writing code in a function -that doesn’t return `Result`, and you want to use `?` when you call other +function that returns `Result` or `Option` or another type that implements +`std::ops::Try`. When you’re writing code in a function +that doesn’t return one of these types, and you want to use `?` when you call other functions that return `Result`, you have two choices to fix this problem. One technique is to change the return type of your function to be `Result` if you have no restrictions preventing that. The other technique is to use diff --git a/src/ch10-00-generics.md b/src/ch10-00-generics.md index a928b81e7c..b411210be2 100644 --- a/src/ch10-00-generics.md +++ b/src/ch10-00-generics.md @@ -125,7 +125,7 @@ number in two different lists. fn largest(list: &[i32]) -> i32 { let mut largest = list[0]; - for &item in list.iter() { + for &item in list { if item > largest { largest = item; } diff --git a/src/ch10-01-syntax.md b/src/ch10-01-syntax.md index 6134de7418..f69165e269 100644 --- a/src/ch10-01-syntax.md +++ b/src/ch10-01-syntax.md @@ -207,8 +207,8 @@ error[E0308]: mismatched types --> src/main.rs:7:38 | 7 | let wont_work = Point { x: 5, y: 4.0 }; - | ^^^ expected integral variable, found -floating-point variable + | ^^^ expected integer, found +floating-point number | = note: expected type `{integer}` found type `{float}` diff --git a/src/ch10-02-traits.md b/src/ch10-02-traits.md index 8fcf15df01..19e873bbac 100644 --- a/src/ch10-02-traits.md +++ b/src/ch10-02-traits.md @@ -611,12 +611,12 @@ reduce duplication but also specify to the compiler that we want the generic type to have particular behavior. The compiler can then use the trait bound information to check that all the concrete types used with our code provide the correct behavior. In dynamically typed languages, we would get an error at -runtime if we called a method on a type that the type didn’t implement. But -Rust moves these errors to compile time so we’re forced to fix the problems -before our code is even able to run. Additionally, we don’t have to write code -that checks for behavior at runtime because we’ve already checked at compile -time. Doing so improves performance without having to give up the flexibility -of generics. +runtime if we called a method on a type which didn’t implement the type which +defines the method. But Rust moves these errors to compile time so we’re forced +to fix the problems before our code is even able to run. Additionally, we don’t +have to write code that checks for behavior at runtime because we’ve already +checked at compile time. Doing so improves performance without having to give +up the flexibility of generics. Another kind of generic that we’ve already been using is called *lifetimes*. Rather than ensuring that a type has the behavior we want, lifetimes ensure diff --git a/src/ch11-03-test-organization.md b/src/ch11-03-test-organization.md index bc8255e306..2af6a0e0f5 100644 --- a/src/ch11-03-test-organization.md +++ b/src/ch11-03-test-organization.md @@ -132,7 +132,7 @@ fn it_adds_two() { `adder` crate We’ve added `use adder` at the top of the code, which we didn’t need in the -unit tests. The reason is that each test in the `tests` directory is a separate +unit tests. The reason is that each file in the `tests` directory is a separate crate, so we need to bring our library into each test crate’s scope. We don’t need to annotate any code in *tests/integration_test.rs* with diff --git a/src/ch12-04-testing-the-librarys-functionality.md b/src/ch12-04-testing-the-librarys-functionality.md index 2d1eeb04d6..e8ed858dec 100644 --- a/src/ch12-04-testing-the-librarys-functionality.md +++ b/src/ch12-04-testing-the-librarys-functionality.md @@ -196,7 +196,7 @@ pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { The `lines` method returns an iterator. We’ll talk about iterators in depth in -[Chapter 13][ch13], but recall that you saw this way of using an +[Chapter 13][ch13-iterators], but recall that you saw this way of using an iterator in [Listing 3-5][ch3-iter], where we used a `for` loop with an iterator to run some code on each item in a collection. @@ -266,7 +266,7 @@ At this point, we could consider opportunities for refactoring the implementation of the search function while keeping the tests passing to maintain the same functionality. The code in the search function isn’t too bad, but it doesn’t take advantage of some useful features of iterators. We’ll -return to this example in [Chapter 13][ch13], where we’ll +return to this example in [Chapter 13][ch13-iterators], where we’ll explore iterators in detail, and look at how to improve it. #### Using the `search` Function in the `run` Function @@ -336,3 +336,4 @@ ch10-03-lifetime-syntax.html#validating-references-with-lifetimes [ch11-anatomy]: ch11-01-writing-tests.html#the-anatomy-of-a-test-function [ch10-lifetimes]: ch10-03-lifetime-syntax.html [ch3-iter]: ch03-05-control-flow.html#looping-through-a-collection-with-for +[ch13-iterators]: ch13-02-iterators.html diff --git a/src/ch13-01-closures.md b/src/ch13-01-closures.md index 05c97c7e6f..56f8ed4586 100644 --- a/src/ch13-01-closures.md +++ b/src/ch13-01-closures.md @@ -133,11 +133,9 @@ The first `if` block calls `simulated_expensive_calculation` twice, the `if` inside the outer `else` doesn’t call it at all, and the code inside the second `else` case calls it once. - - The desired behavior of the `generate_workout` function is to first check -whether the user wants a low-intensity workout (indicated by a number less -than 25) or a high-intensity workout (a number of 25 or greater). +whether the user wants a low-intensity workout (indicated by a number less than +25) or a high-intensity workout (a number of 25 or greater). Low-intensity workout plans will recommend a number of push-ups and sit-ups based on the complex algorithm we’re simulating. @@ -402,7 +400,7 @@ error[E0308]: mismatched types | | let n = example_closure(5); | ^ expected struct `std::string::String`, found - integral variable + integer | = note: expected type `std::string::String` found type `{integer}` diff --git a/src/ch14-01-release-profiles.md b/src/ch14-01-release-profiles.md index c150f6a073..ab2400511e 100644 --- a/src/ch14-01-release-profiles.md +++ b/src/ch14-01-release-profiles.md @@ -66,4 +66,4 @@ Cargo will use the defaults for the `dev` profile plus our customization to optimizations than the default, but not as many as in a release build. For the full list of configuration options and defaults for each profile, see -[Cargo’s documentation](https://doc.rust-lang.org/cargo/). +[Cargo’s documentation](https://doc.rust-lang.org/cargo/reference/manifest.html#the-profile-sections). diff --git a/src/ch14-03-cargo-workspaces.md b/src/ch14-03-cargo-workspaces.md index 8b8c078baf..a662ac219e 100644 --- a/src/ch14-03-cargo-workspaces.md +++ b/src/ch14-03-cargo-workspaces.md @@ -192,12 +192,17 @@ each other. Let’s add the `rand` crate to the `[dependencies]` section in the *add-one/Cargo.toml* file to be able to use the `rand` crate in the `add-one` crate: + + Filename: add-one/Cargo.toml ```toml [dependencies] - -rand = "0.3.14" +rand = "0.5.5" ``` We can now add `use rand;` to the *add-one/src/lib.rs* file, and building the @@ -206,10 +211,10 @@ and compile the `rand` crate: ```text $ cargo build - Updating registry `https://github.com/rust-lang/crates.io-index` - Downloading rand v0.3.14 + Updating crates.io index + Downloaded rand v0.5.5 --snip-- - Compiling rand v0.3.14 + Compiling rand v0.5.5 Compiling add-one v0.1.0 (file:///projects/add/add-one) Compiling adder v0.1.0 (file:///projects/add/adder) Finished dev [unoptimized + debuginfo] target(s) in 10.18 secs diff --git a/src/ch15-02-deref.md b/src/ch15-02-deref.md index 6520eaf96c..e0fae5b1a9 100644 --- a/src/ch15-02-deref.md +++ b/src/ch15-02-deref.md @@ -10,7 +10,7 @@ Let’s first look at how the dereference operator works with regular references Then we’ll try to define a custom type that behaves like `Box`, and see why the dereference operator doesn’t work like a reference on our newly defined type. We’ll explore how implementing the `Deref` trait makes it possible for -smart pointers to work in a similar way as references. Then we’ll look at +smart pointers to work in ways similar to references. Then we’ll look at Rust’s *deref coercion* feature and how it lets us work with either references or smart pointers. diff --git a/src/ch15-03-drop.md b/src/ch15-03-drop.md index 800de36bfc..333e4e2c1d 100644 --- a/src/ch15-03-drop.md +++ b/src/ch15-03-drop.md @@ -58,7 +58,7 @@ an instance of your type goes out of scope. We’re printing some text here to demonstrate when Rust will call `drop`. In `main`, we create two instances of `CustomSmartPointer` and then print -`CustomSmartPointers created.`. At the end of `main`, our instances of +`CustomSmartPointers created`. At the end of `main`, our instances of `CustomSmartPointer` will go out of scope, and Rust will call the code we put in the `drop` method, printing our final message. Note that we didn’t need to call the `drop` method explicitly. @@ -84,7 +84,7 @@ functionality. Disabling `drop` isn’t usually necessary; the whole point of th `Drop` trait is that it’s taken care of automatically. Occasionally, however, you might want to clean up a value early. One example is when using smart pointers that manage locks: you might want to force the `drop` method that -releases the lock to run so other code in the same scope can acquire the lock. +releases the lock so that other code in the same scope can acquire the lock. Rust doesn’t let you call the `Drop` trait’s `drop` method manually; instead you have to call the `std::mem::drop` function provided by the standard library if you want to force a value to be dropped before the end of its scope. @@ -146,7 +146,7 @@ an argument. The function is in the prelude, so we can modify `main` in Listing # # impl Drop for CustomSmartPointer { # fn drop(&mut self) { -# println!("Dropping CustomSmartPointer!"); +# println!("Dropping CustomSmartPointer with data `{}`!", self.data); # } # } # diff --git a/src/ch15-05-interior-mutability.md b/src/ch15-05-interior-mutability.md index f43d549244..34c002b2fa 100644 --- a/src/ch15-05-interior-mutability.md +++ b/src/ch15-05-interior-mutability.md @@ -1,16 +1,14 @@ ## `RefCell` and the Interior Mutability Pattern - - *Interior mutability* is a design pattern in Rust that allows you to mutate data even when there are immutable references to that data; normally, this action is disallowed by the borrowing rules. To mutate data, the pattern uses `unsafe` code inside a data structure to bend Rust’s usual rules that govern -mutation and borrowing. We haven’t yet covered unsafe code; we will in -Chapter 19. We can use types that use the interior mutability pattern when we -can ensure that the borrowing rules will be followed at runtime, even though -the compiler can’t guarantee that. The `unsafe` code involved is then wrapped -in a safe API, and the outer type is still immutable. +mutation and borrowing. We haven’t yet covered unsafe code; we will in Chapter +19. We can use types that use the interior mutability pattern when we can +ensure that the borrowing rules will be followed at runtime, even though the +compiler can’t guarantee that. The `unsafe` code involved is then wrapped in a +safe API, and the outer type is still immutable. Let’s explore this concept by looking at the `RefCell` type that follows the interior mutability pattern. diff --git a/src/ch16-02-message-passing.md b/src/ch16-02-message-passing.md index 7cb4cfa8d3..6b5c23f87e 100644 --- a/src/ch16-02-message-passing.md +++ b/src/ch16-02-message-passing.md @@ -35,12 +35,11 @@ want to send over the channel. Filename: src/main.rs -```rust +```rust,ignore,does_not_compile use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); -# tx.send(()).unwrap(); } ``` @@ -56,16 +55,14 @@ of the streams will end up in one river at the end. We’ll start with a single producer for now, but we’ll add multiple producers when we get this example working. - - The `mpsc::channel` function returns a tuple, the first element of which is the sending end and the second element is the receiving end. The abbreviations `tx` and `rx` are traditionally used in many fields for *transmitter* and *receiver* respectively, so we name our variables as such to indicate each end. We’re using a `let` statement with a pattern that destructures the tuples; we’ll -discuss the use of patterns in `let` statements and destructuring in -Chapter 18. Using a `let` statement this way is a convenient approach to -extract the pieces of the tuple returned by `mpsc::channel`. +discuss the use of patterns in `let` statements and destructuring in Chapter +18. Using a `let` statement this way is a convenient approach to extract the +pieces of the tuple returned by `mpsc::channel`. Let’s move the transmitting end into a spawned thread and have it send one string so the spawned thread is communicating with the main thread, as shown in diff --git a/src/ch16-03-shared-state.md b/src/ch16-03-shared-state.md index e5491d147c..7050d4db15 100644 --- a/src/ch16-03-shared-state.md +++ b/src/ch16-03-shared-state.md @@ -2,7 +2,7 @@ Message passing is a fine way of handling concurrency, but it’s not the only one. Consider this part of the slogan from the Go language documentation again: -“communicate by sharing memory.” +“do not communicate by sharing memory.” What would communicating by sharing memory look like? In addition, why would message-passing enthusiasts not use it and do the opposite instead? diff --git a/src/ch17-02-trait-objects.md b/src/ch17-02-trait-objects.md index 6145f21c52..d7369bffa1 100644 --- a/src/ch17-02-trait-objects.md +++ b/src/ch17-02-trait-objects.md @@ -275,14 +275,15 @@ new type and draw it because `SelectBox` implements the `Draw` trait, which means it implements the `draw` method. This concept—of being concerned only with the messages a value responds to -rather than the value’s concrete type—is similar to the concept *duck typing* -in dynamically typed languages: if it walks like a duck and quacks like a duck, -then it must be a duck! In the implementation of `run` on `Screen` in Listing -17-5, `run` doesn’t need to know what the concrete type of each component is. -It doesn’t check whether a component is an instance of a `Button` or a -`SelectBox`, it just calls the `draw` method on the component. By specifying -`Box` as the type of the values in the `components` vector, we’ve -defined `Screen` to need values that we can call the `draw` method on. +rather than the value’s concrete type—is similar to the concept of *duck +typing* in dynamically typed languages: if it walks like a duck and quacks +like a duck, then it must be a duck! In the implementation of `run` on `Screen` +in Listing 17-5, `run` doesn’t need to know what the concrete type of each +component is. It doesn’t check whether a component is an instance of a `Button` +or a `SelectBox`, it just calls the `draw` method on the component. By +specifying `Box` as the type of the values in the `components` +vector, we’ve defined `Screen` to need values that we can call the `draw` +method on. The advantage of using trait objects and Rust’s type system to write code similar to code using duck typing is that we never have to check whether a diff --git a/src/ch17-03-oo-design-patterns.md b/src/ch17-03-oo-design-patterns.md index efe3f43943..4ffb3a0179 100644 --- a/src/ch17-03-oo-design-patterns.md +++ b/src/ch17-03-oo-design-patterns.md @@ -380,7 +380,7 @@ otherwise, we want to return an empty string slice, as shown in Listing 17-17: impl Post { // --snip-- pub fn content(&self) -> &str { - self.state.as_ref().unwrap().content(&self) + self.state.as_ref().unwrap().content(self) } // --snip-- } diff --git a/src/ch18-02-refutability.md b/src/ch18-02-refutability.md index 55cb03812d..36fa17d48b 100644 --- a/src/ch18-02-refutability.md +++ b/src/ch18-02-refutability.md @@ -10,8 +10,9 @@ a_value` because if the value in the `a_value` variable is `None` rather than Function parameters, `let` statements, and `for` loops can only accept irrefutable patterns, because the program cannot do anything meaningful when -values don’t match. The `if let` and `while let` expressions only accept -refutable patterns, because by definition they’re intended to handle possible +values don’t match. The `if let` and `while let` expressions accept +refutable and irrefutable patterns, but the compiler warns against +irrefutable patterns because by definition they’re intended to handle possible failure: the functionality of a conditional is in its ability to perform differently depending on success or failure. @@ -69,9 +70,9 @@ patterns instead of `let` We’ve given the code an out! This code is perfectly valid, although it means we cannot use an irrefutable pattern without receiving an error. If we give `if let` a pattern that will always match, such as `x`, as shown in Listing 18-10, -it will not compile. +the compiler will give a warning. -```rust,ignore,does_not_compile +```rust,ignore if let x = 5 { println!("{}", x); }; @@ -84,11 +85,15 @@ Rust complains that it doesn’t make sense to use `if let` with an irrefutable pattern: ```text -error[E0162]: irrefutable if-let pattern - --> :2:8 +warning: irrefutable if-let pattern + --> :2:5 + | +2 | / if let x = 5 { +3 | | println!("{}", x); +4 | | }; + | |_^ | -2 | if let x = 5 { - | ^ irrefutable pattern + = note: #[warn(irrefutable_let_patterns)] on by default ``` For this reason, match arms must use refutable patterns, except for the last diff --git a/src/ch18-03-pattern-syntax.md b/src/ch18-03-pattern-syntax.md index ccdeb6608b..31b96a88b3 100644 --- a/src/ch18-03-pattern-syntax.md +++ b/src/ch18-03-pattern-syntax.md @@ -105,9 +105,9 @@ match x { This code prints `one or two`. -### Matching Ranges of Values with `...` +### Matching Ranges of Values with `..=` -The `...` syntax allows us to match to an inclusive range of values. In the +The `..=` syntax allows us to match to an inclusive range of values. In the following code, when a pattern matches any of the values within the range, that arm will execute: @@ -115,14 +115,14 @@ arm will execute: let x = 5; match x { - 1...5 => println!("one through five"), + 1..=5 => println!("one through five"), _ => println!("something else"), } ``` If `x` is 1, 2, 3, 4, or 5, the first arm will match. This syntax is more convenient than using the `|` operator to express the same idea; instead of -`1...5`, we would have to specify `1 | 2 | 3 | 4 | 5` if we used `|`. +`1..=5`, we would have to specify `1 | 2 | 3 | 4 | 5` if we used `|`. Specifying a range is much shorter, especially if we want to match, say, any number between 1 and 1,000! @@ -136,8 +136,8 @@ Here is an example using ranges of `char` values: let x = 'c'; match x { - 'a'...'j' => println!("early ASCII letter"), - 'k'...'z' => println!("late ASCII letter"), + 'a'..='j' => println!("early ASCII letter"), + 'k'..='z' => println!("late ASCII letter"), _ => println!("something else"), } ``` @@ -711,11 +711,11 @@ fn main() { match x { Some(50) => println!("Got 50"), - Some(n) if n == y => println!("Matched, n = {:?}", n), + Some(n) if n == y => println!("Matched, n = {}", n), _ => println!("Default case, x = {:?}", x), } - println!("at the end: x = {:?}, y = {:?}", x, y); + println!("at the end: x = {:?}, y = {}", x, y); } ``` @@ -783,7 +783,7 @@ were applied only to the final value in the list of values specified using the The *at* operator (`@`) lets us create a variable that holds a value at the same time we’re testing that value to see whether it matches a pattern. Listing 18-29 shows an example where we want to test that a `Message::Hello` `id` field -is within the range `3...7`. But we also want to bind the value to the variable +is within the range `3..=7`. But we also want to bind the value to the variable `id_variable` so we can use it in the code associated with the arm. We could name this variable `id`, the same as the field, but for this example we’ll use a different name. @@ -796,10 +796,10 @@ enum Message { let msg = Message::Hello { id: 5 }; match msg { - Message::Hello { id: id_variable @ 3...7 } => { + Message::Hello { id: id_variable @ 3..=7 } => { println!("Found an id in range: {}", id_variable) }, - Message::Hello { id: 10...12 } => { + Message::Hello { id: 10..=12 } => { println!("Found an id in another range") }, Message::Hello { id } => { @@ -812,7 +812,7 @@ match msg { while also testing it This example will print `Found an id in range: 5`. By specifying `id_variable -@` before the range `3...7`, we’re capturing whatever value matched the range +@` before the range `3..=7`, we’re capturing whatever value matched the range while also testing that the value matched the range pattern. In the second arm, where we only have a range specified in the pattern, the code diff --git a/src/ch19-01-unsafe-rust.md b/src/ch19-01-unsafe-rust.md index 8605ede6a2..c7956e8627 100644 --- a/src/ch19-01-unsafe-rust.md +++ b/src/ch19-01-unsafe-rust.md @@ -33,6 +33,7 @@ the ability to: * Call an unsafe function or method * Access or modify a mutable static variable * Implement an unsafe trait +* Access fields of `union`s It’s important to understand that `unsafe` doesn’t turn off the borrow checker or disable any other of Rust’s safety checks: if you use a reference in unsafe @@ -250,7 +251,7 @@ fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { This function first gets the total length of the slice. Then it asserts that the index given as a parameter is within the slice by checking whether it’s less than or equal to the length. The assertion means that if we pass an index -that is greater than the index to split the slice at, the function will panic +that is greater than the length to split the slice at, the function will panic before it attempts to use that index. Then we return two mutable slices in a tuple: one from the start of the diff --git a/src/ch20-02-multithreaded.md b/src/ch20-02-multithreaded.md index 2e64463486..1f80bbb208 100644 --- a/src/ch20-02-multithreaded.md +++ b/src/ch20-02-multithreaded.md @@ -364,7 +364,7 @@ impl ThreadPool { ``` We still use the `()` after `FnOnce` because this `FnOnce` represents a closure -that takes no parameters and doesn’t return a value. Just like function +that takes no parameters and returns the unit type `()`. Just like function definitions, the return type can be omitted from the signature, but even if we have no parameters, we still need the parentheses. @@ -895,7 +895,7 @@ at Listing 20-19. # use std::sync::mpsc; # struct Worker {} -type Job = Box; +type Job = Box; impl ThreadPool { // --snip-- diff --git a/src/title-page.md b/src/title-page.md index bc7fb9031b..5e375d31b4 100644 --- a/src/title-page.md +++ b/src/title-page.md @@ -2,7 +2,7 @@ *by Steve Klabnik and Carol Nichols, with contributions from the Rust Community* -This version of the text assumes you’re using Rust 1.31.0 or later with +This version of the text assumes you’re using Rust 1.37.0 or later with `edition="2018"` in *Cargo.toml* of all projects to use Rust 2018 Edition idioms. See the [“Installation” section of Chapter 1][install] to install or update Rust, and see the new [Appendix E][editions]