diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2cb69b4ab02eec99607328a0280f843fd4ca4f95..6f0fb81a2cb396a609b103a6ace8b12632afbe57 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,4 +19,5 @@ run_test_doc: - cd codes - ./run_tests.sh - cd ../book + - mdbook test - mdbook build diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 99b1e890d5924a70d36a9347c3ed2e9dd709433d..a5ffa5285f7e95ae9e534065fe1ddb9152eee0bf 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -1,3 +1,4 @@ # Summary - [Part 00](./part00.md) +- [Part 01](./part01.md) diff --git a/book/src/part01.md b/book/src/part01.md new file mode 100644 index 0000000000000000000000000000000000000000..ca4c6af53b0f0fe6cfb18c4b7aa1afbeb79aa805 --- /dev/null +++ b/book/src/part01.md @@ -0,0 +1,255 @@ +# Discussion du code `part01` + +## Concepts + +Les concepts abordés dans cet exemple sont: + +1. [Les fonctions.](#les-fonctions) +2. [Les `if ... else` sont des **expressions**.](#en-rust-le-if-est-une-expression) +3. [Itérer sur les éléments d'un tableaux directement avec la boucle for.](#la-boucle-for-revisitée) + +## Discussion + +### Les fonctions + +Dans cette seconde itération de notre programme calculant le minimum d'une liste de nombres, +nous introduisons les **fonctions** en Rust. + +La syntaxe d'une fonction est la suivante: +```rust,ignore +fn function_name(arg1: type, arg2: type, ...) -> ReturnType +``` +Une fonction est annotée avec le mot-clé `fn` suivi de son identifiant. +Son type de retour se trouve à droite après la *flèche* (en ASCII-art) `->`. +Si une fonction n'a pas de type de retour explicite (il n'y a pas de flèche) le type de retour est `()` qui +rappelle le type `void` de C (aka sans type, mais oui c'est un type en Rust). Entre parenthèses se trouvent les arguments de la fonction suivi de leur type. Le nombre d'arguments est supérieur ou égal à zéro. + +Une fonction est ensuite appelée par son identifiant et ses éventuels arguments. + +```rust,no_run +const SIZE: usize = 9; +fn read_command_line() -> [i32; SIZE] { + [10, 32, 12, 43, 52, 53, 83, 2, 9] + // équivalent à return [10, 32, 12, 43, 52, 53, 83, 2, 9]; +} +fn main() { + read_command_line(); +} +``` +La fonction `read_command_line()` ne prend ainsi aucun argument et retourne un tableau statique d'entier 32 bits et de taille `SIZE`. +Pour qu'une fonction retourne une valeur, on a deux solutions: +1. Si la valeur est la dernière expression de la fonction, on peut juste mettre la valeur (ou la variable) sans point virgule à la fin de la ligne. +2. On peut, de façon similaire au C, utiliser le mot clé `return val;` +On constate ici que les tableaux statiques (contrairement à ce qui se passe dans le langage C) peuvent être retournés +par une fonction (le tableau n'est pas désalloué au moment du retour). + +La vérification de la taille du tableau se fait dans la fonction `check_size(size: usize)` qui prend donc un argument de +type `usize` et ne retourne rien. + +```rust,should_panic +fn check_size(size: usize) { + # if size == 0 { + # panic!("Size is of tab = 0."); + # } +} +# fn main() { +check_size(10); // runs alright +check_size(0); // panics +# } +``` + +À l'inverse la fonction `print_tab(tab)` prend en argument un tableau statique et ne retourne rien. + +```rust +# const SIZE: usize = 9; +# fn read_command_line() -> [i32; SIZE] { +# [10, 32, 12, 43, 52, 53, 83, 2, 9] +# } +fn print_tab(tab: [i32; SIZE]) { + for t in tab { + print!("{} ", t); + } + println!(); +} +# fn main() { +let tab = read_command_line(); +print_tab(tab); +# } +``` + +Les fonctions `min_i32(lhs, rhs)` retourne la plus petite valeur de deux entiers signés de 32 bits. + +```rust +fn min_i32(lhs: i32, rhs: i32) -> i32 { + if lhs < rhs { + lhs + } else { + rhs + } +} +# fn main() { +let min = min_i32(2, 10); +println!("{min}"); +# } +``` + +### En Rust le `if` est une expression + +Mais si vous étudiez attentivement ce code, on constate que le corps de la fonction est étrange. +On voit qu'aucune ligne ne possède de point terminal. Cela voudrait dire que `lhs` ou `rhs` sont la dernière expression +de la fonction. En fait ce qui se passe c'est que le `if ... else` est une **expression**. De façon similaire au +`if` ternaire en `C`, le `if ... else` en Rust retourne une valeur. On peut écrire de façon équivalente au code ci-dessus +```rust +fn min_i32(lhs: i32, rhs: i32) -> i32 { + let min = if lhs < rhs { + lhs + } else { + rhs + }; + min +} +# fn main() { +let min = min_i32(2, 10); +println!("{min}"); +# } +``` +On voit qu'ici à la fin du bloc du `else` nous avons un point virgule qui indique la fin de la ligne. +Ainsi `lhs` ou `rhs` sont liés à la variable immutable `min`. + +Cette notion d'expression est très importante. Comme nous l'avons expliqué précédemment la dernière expression d'une fonction est évaluée puis retournée par cette dernière. + +Un bloc de code qui produit un résultat est une expression. + +En Rust, plusieurs structures de contrôle tel que le `If` sont des expression. Il n'est pas nécessaire de connaître la valeur de l'expression pour la retourner. + +Nous pourrions considérer par exemple les fonctions suivante : + +```rust,no_run +fn add(lhs: i32, rhs: i32) -> i32 { + lhs + rhs +} + +fn add_one(val: i32) -> i32 { + add(1, val) +} +``` +### La boucle `for` revisitée + +La dernière fonction à discuter est `find_min(tab)` qui retourne la valeur minimale se trouvant dans `tab`. + +```rust +# const SIZE: usize = 9; +# fn read_command_line() -> [i32; SIZE] { +# [10, 32, 12, 43, 52, 53, 83, 2, 9] +# } +# fn check_size(size: usize) { +# if size == 0 { +# panic!("Size is of tab = 0."); +# } +# } +# fn min_i32(lhs: i32, rhs: i32) -> i32 { +# if lhs < rhs { +# lhs +# } else { +# rhs +# } +# } +fn find_min(tab: [i32; SIZE]) -> i32 { + check_size(SIZE); + let mut min = i32::MAX; + for t in tab { + min = min_i32(min, t); + } + min +} +# fn main() { +let min = find_min(read_command_line()); +println!("The minimal value is: {min}"); +# } +``` +Cette version du calcul du minimum contient une différence importante dans la syntaxe de la boucle `for`. +En effet, ici on itère pas sur un indice et on accède ensuite aux éléments du tableau. +La syntaxe +```rust,ignore +for val in tab +``` +permet d'itérer directement sur les valeurs contenues dans `tab`. Au début de chaque tour de la boucle `for`, +val prend la valeur "courante" du tableau. A la fin du bloc du `for`, la valeur courante prend la valeur "suivante" +dans le tableau si elle existe. En fait le tableau est implicitement converti en *itérateur* (plus de détails plus tard sur le sujet). + +Dans d'autres langages on appelle ça une boucle "for each" qui traduit en français signifie *pour chaque*. + +Le code ci-dessus est strictement équivalent à + +```rust +# const SIZE: usize = 9; +# fn read_command_line() -> [i32; SIZE] { +# [10, 32, 12, 43, 52, 53, 83, 2, 9] +# } +# fn check_size(size: usize) { +# if size == 0 { +# panic!("Size is of tab = 0."); +# } +# } +# fn min_i32(lhs: i32, rhs: i32) -> i32 { +# if lhs < rhs { +# lhs +# } else { +# rhs +# } +# } +fn find_min(tab: [i32; SIZE]) -> i32 { + check_size(SIZE); + let mut min = i32::MAX; + for i in 0..SIZE { + min = min_i32(min, tab[i]); + } + min +} +# fn main() { +let min = find_min(read_command_line()); +println!("The minimal value is: {min}"); +# } +``` + +## Le code + +```rust +# const SIZE: usize = 9; +# fn read_command_line() -> [i32; SIZE] { +# [10, 32, 12, 43, 52, 53, 83, 2, 9] +# } +# fn check_size(size: usize) { +# if size == 0 { +# panic!("Size is of tab = 0."); +# } +# } +# fn print_tab(tab: [i32; SIZE]) { +# for t in tab { +# print!("{} ", t); +# } +# println!(); +# } +# fn min_i32(lhs: i32, rhs: i32) -> i32 { +# if lhs < rhs { +# lhs +# } else { +# rhs +# } +# } +# fn find_min(tab: [i32; SIZE]) -> i32 { +# check_size(SIZE); +# let mut min = i32::MAX; +# for t in tab { +# min = min_i32(min, t); +# } +# min +# } +fn main() { + let tab = read_command_line(); + println!("Among the numbers in the list:"); + print_tab(tab); + let min = find_min(tab); + println!("The minimal value is: {min}"); +} +``` diff --git a/codes/rust_lang/part01/src/main.rs b/codes/rust_lang/part01/src/main.rs index 44065235cad65b8b64c5d1418ed21233ec2e7ffa..832223edf08d207d22b6abcc2cfeb86cd9f78eb9 100644 --- a/codes/rust_lang/part01/src/main.rs +++ b/codes/rust_lang/part01/src/main.rs @@ -3,8 +3,6 @@ /// - Arguments are "moved" /// - if is an expression /// - for loops revisited -/// - Tuples -/// - Destructuring const SIZE: usize = 9; @@ -21,12 +19,11 @@ fn check_size(size: usize) { // Prints tab and returns tab. // Tab would be destructed at the end of the function otherwise. -fn print_tab(tab: [i32; SIZE]) -> [i32; SIZE] { +fn print_tab(tab: [i32; SIZE]) { for t in tab { print!("{} ", t); } println!(); - tab } fn min_i32(lhs: i32, rhs: i32) -> i32 { @@ -37,23 +34,19 @@ fn min_i32(lhs: i32, rhs: i32) -> i32 { } } -fn find_min(tab: [i32; SIZE], size: usize) -> ([i32; SIZE], i32) { - check_size(size); +fn find_min(tab: [i32; SIZE]) -> i32 { + check_size(SIZE); let mut min = i32::MAX; for t in tab { min = min_i32(min, t); } - (tab, min) + min } fn main() { let tab = read_command_line(); - println!("Among the numbers in the list:"); - let tab = print_tab(tab); - - // There are alternatives to access fields of tuples - let (_, min) = find_min(tab, SIZE); - // The first field is not used therefore we can replace it with "_" + print_tab(tab); + let min = find_min(tab); println!("The minimal value is: {}", min); }