diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 718d00bd83710e40deada98bb6803df45e8ca210..bbcd71961de0b9cc279a9c1664ed13d6e8ccd0de 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -14,5 +14,7 @@ - [Part 09](./part09.md) - [Part 10](./part10.md) - [Part 11](./part11.md) +- [Les collections](./collections.md) +- [Lifetimes](./lifetimes.md) - [CLI](./cli.md) - [Part 12](./part12.md) diff --git a/book/src/collections.md b/book/src/collections.md new file mode 100644 index 0000000000000000000000000000000000000000..cd901fc2127dd34bc0dec7d50087dd56df41169e --- /dev/null +++ b/book/src/collections.md @@ -0,0 +1,194 @@ +# Les collections + +## Concepts + +Les concepts abordés dans cet exemple sont: + +- [Les collections](#les-collections) + - [Concepts](#concepts) + - [Documentation](#documentation) + - [Discussion](#discussion) + - [Le type `Vec`](#le-type-vect) + - [Le type `String`](#le-type-string) + - [Les slices](#les-slices) + +## Documentation + +Afin de compléter ce cours, je vous recommande la lecture des ressources suivantes : + +- [Les Vec](https://doc.rust-lang.org/book/ch08-01-vectors.html) +- [Les String](https://doc.rust-lang.org/book/ch08-02-strings.html) +- [Les slices](https://doc.rust-lang.org/book/ch04-03-slices.html) + +## Discussion + +En Rust, comme dans la plupart des langages modernes, il existe des structures de données qui permettent +de se simplifier la vie lors de l'implémentation de divers algorithmes. + +Dans ce chapitre, nous allons discuter des types `Vec<T>`, `String`, des slices. + +Dans ce code nous modifions que très peu le code [de la partie 6](part06.md) afin +d'utiliser les types `Vec<i32>`, `String` et les slices. + +### Le type `Vec<T>` + +Le type `Vec<T>` est une collection qui permet de stocker des données d'un type unique générique, `T`. +Ces données sont stockées dans un espace mémoire qui est garanti d'être contigu. Cet espace mémoire +peut changer dynamiquement à l'exécution contrairement aux tableaux statiques. D'un certain point +de vue, on peut le considérer comme un "pointeur intelligent": un `Vec` est un pointeur sur le tas, +qui a une certaine capacité mémoire et sait combien la mémoire sur laquelle il pointe +est pleine. + +Dans notre code, nous avons créé une fonction `read_command_line()` +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:read_command_line}} +``` +Ici, un `Vec<i32>` est instancié et on ajoute des éléments aléatoires dedans à l'aide de la +crate `rand()`. +Pour créer un `Vec` vide, on utilise la fonction associée, qui crée un vecteur vide +```rust +{{#include ../../codes/rust_lang/collections/src/io.rs:vec_new}} +``` +Ensuite, on remplit le vecteur avec des nombres aléatoires à l'aide +d'une boucle `for` +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:vec_for}} +``` +qui itère sur les indices allant de `0` à `len-1`. +Comme, nous n'utilisons pas l'indice, nous l'ignorons à l'aide de l'annotation `_i`. +On ajoute des éléments dans le vecteur avec la fonction `push()` et comme nous modifions +`v` il est primordial de noter qu'il est `mut`-able. Le générateur de nombre aléatoire +est stocké dans une variable `rng` qui doit elle aussi être mutable. +En effet, les générateurs de nombres aléatoires stockent en général un état interne. + +### Le type `String` + +Une `String` ou un chaîne de caractère, est une séquence de caractères `UTF-8`. +On pourrait penser naïvement que ce n'est rien d'autre qu'un `Vec<char>`: en fait c'est plus compliqué que cela. +Bien que la chaîne de caractères, soit également rien d'autre qu'un pointeur vers des données sur le tas, +ainsi qu'une variable contenant la capacité de la mémoire sur laquelle pointe le pointeur et son remplissage. + +Le problème principal vient de l'encodage `UTF-8`: c'est un encodage de taille variable qui englobe les caractères +ASCII (qui sont stockés sur un octet) et une très grande quantité d'autres caractères (l'alphabet grec, des emojis, etc.) +qui sont stockés sur 1 à 4 octets. Ainsi chaque lettre de la chaîne de caractère correspond à un nombre variable d'octets +(ce qui n'est pas le cas pour un `Vec`). Il est donc très vivement **déconseillé** de tenter d'indexer +une `String` (faire `s[i]`) comme on le ferait avec un `Vec`. Il faut plutôt utiliser la méthode `.get(i)` qui +interprète les caractères en fonction de la longueur de leurs encodages. + +Une illustration de l'utilisation d'une chaîne de caractère se trouve à la fonction +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:read_command_line_str}} +``` +Cette fonction construit une chaîne de caractères constituées de nombres et d'espaces, +puis la transforme en `Vec<i32>` dont on calculera ensuite le minimum. + +Une `String` est souvent construite à partir d'une "chaîne littérale" à l'aide du trait de conversion `From` +(on les reconnaît parce qu'elles sont entourées de guillemets, `""`) +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:from}} +``` +dont le type est `str` formellement (c'est une chaîne de caractères qui vit durant toute la durée de vie du programme). +Néanmoins, le type `str` n'est jamais utilisé en tant que tel en Rust, mais on utilise plutôt son "slice" +`&str` (plus sur les "tranches" dans la [section suivante](#les-slices)). + +Nous voyons que dans le code ci-dessus nous avons déclaré la variable `s` comme étant mutable, car ensuite nous ajoutons +une slice de chaîne de caractère (de type `&str`) à l'aide de la fonction `push_str()` +dans `s` +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:push_str}} +``` +On peut également ajouter des `char` (ils sont entourés d'apostrophes `' '`) à l'aide de la fonction `push()` +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:push_char}} +``` +Ensuite cette chaîne de caractères est convertie en `Vec<&str>` où chaque élément du `Vec` est un mot, qui est une sous chaîne de `s`. +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:split}} +``` +Finalement, dans +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:string_for}} +``` +on crée un nouveau `Vec<i32>` dans lequel on ajoute les mots convertis en entiers. +On commence par itérer sur le `Vec<&str>` en utilisant les indices de `0` à `s.len()-1` (la longueur du `Vec` `s`). +Puis nous passons à la conversion à proprement parler, bien qu'on fasse des choses un peu compliquées +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:conversion}} +``` +Ici, on commence par récupérer le `i`-ème index de `s` à l'aide de la méthode `get(i)` qui retourne +une `Option<&str>` (si `i` est un indice valide nous avons `Some(s[i])`, sinon `None`). Puis, nous transformons +l'option avec `ok_or()` (nous encapsulons `s[i]` dans un `Ok()` si nous avons un `Some()` et transformons +`None`en `Err("Unable to index")`). Ensuite nous "parsons" `s[i]` et retournons une erreur si le parsing échoue. +Si tout s'est bien passé nous faisons donc un `push()` de chaque `i32` et finissons par retourner le `Vec<i32>` +encapsulé dans un `Ok()`. + +### Les slices + +Un slice est une "tranche" de tableau, statique ou dynamique: une référence vers un bout de mémoire et +la longueur de cette mémoire. + +Ainsi, si nous créons un tableau statique, nous pouvons référencer une "tranche" ou slice +avec la syntaxe suivante +```rust +let a = [1, 2, 3, 4, 5, 6, 7, 8]; +let b = &a[1..4]; // on pointe vers [2, 3, 4] +``` +`b` sera donc une référence et saura que la mémoire sur laquelle elle pointe est de longueur `3` +(cette information permet d'éviter les dépassements de capacité). +On notera la syntaxe `x..y` où `y` est non inclus (comme pour la boucle `for` avec les indices). +Il existe également une syntaxe sans bornes à gauche, à droite, ou à gauche et à droite. +```rust +let a = [1, 2, 3, 4, 5, 6, 7, 8]; +let b = &a[1..]; // on pointe vers [2, 3, .., 8] +let b = &a[..5]; // on pointe vers [1, 2, .., 5] +let b = &a[..]; // on pointe vers [1, 2, .., 8] +``` +Cette syntaxe s'applique également pour toute collection qu'on peut indexer +Le type d'un slice est noté par `&[T]`, où `T` est un type. On en voit un exemple +lorsqu'on veut afficher un tableau par exemple +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/io.rs:print_tab}} +``` +ou encore dans la fonction `find_min()` +```rust,ignore +{{#include ../../codes/rust_lang/collections/src/something_or_nothing.rs:find_min}} +``` +Comme on le voit dans le `main()` l'implémentation à l'aide d'un slice dans les fonction +permet une bien plus grande généricité que si on impose un type `Vec`, un tableau statique, +ou un slice. +```rust,ignore +// Vec +{{#include ../../codes/rust_lang/collections/src/main.rs:vec}} +// Slice +{{#include ../../codes/rust_lang/collections/src/main.rs:ref}} +// Array +{{#include ../../codes/rust_lang/collections/src/main.rs:tab}} +``` + +La notation `&str` représente ainsi une référence vers un `str` qui est une chaîne de caractères +littérale, allouée pour la durée entière d'un programme dans une zone dédiée de la mémoire. +Le type `str` étant "immovable" il n'est jamais utilisé tel quel, mais uniquement via des références. + +Comme pour le slice utilisé pour généraliser le passage en argument des tableaux, +le slice de string `&str` est également utilisé pour généraliser le passage de argument +de chaînes de caractères. + +## Rustlings + +Les rustlings à faire dans ce chapitre sont les suivants: + +### Les `Vec` + +```bash +$ rustlings run vecs1 +$ rustlings run vecs2 +``` + +### Les `String` + +```bash +$ rustlings run strings1 +$ rustlings run strings2 +$ rustlings run strings3 +$ rustlings run strings4 +``` diff --git a/book/src/lifetimes.md b/book/src/lifetimes.md new file mode 100644 index 0000000000000000000000000000000000000000..7254b095f5bbff0e8cd0bdf87c0ae36ab28d740d --- /dev/null +++ b/book/src/lifetimes.md @@ -0,0 +1,261 @@ +# Lifetimes + +## Concepts + +Les concepts abordés dans cet exemple sont: + +1. [Le pattern `NewYtpe`](#le-pattern-newtype) +2. [Les lifetimes](#les-lifetimes) +3. [Les itérateurs et la généricité]() + +Pour plus d'informations sur le pattern `NewType`, vous pouvez vous référer aux +chapitres [19.2](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types) et [19.3](https://doc.rust-lang.org/book/ch19-04-advanced-types.html) du livre. +Pour les lifetimes, il y a le [chapitre du livre](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) correspondant, +ainsi que celui du [Rustonomicon](https://doc.rust-lang.org/nomicon/lifetimes.html). + +## Discussion + +Dans ce chapitre, nous allons voir principalement deux concepts différents qui sont importants en Rust +et qu'on retrouve dans beaucoup de code. Les lifetimes, en particulier, sont un sujet complexe +et on verra deux applications différentes mais cela constitue la pointe de l'iceberg des applications +possibles. Le pattern `NewType` est lui bien plus simple, et ne nécessite pas une très longue discussion. + +### Le pattern `NewType` + +Le pattern `NewType` très commun en Rust consiste à encapsuler un type externe à une crate, dans un type local +comme dans +```rust +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:newtype}} +``` +où on encapsule le type externe `Option<T>` dans `SomethingOrNothing<T>`. +Ce type a un paramètre générique `T` et dérive le trait `Debug` qui permet +de faire un affichage détaillé (mais pas très joli) du contenu du type. +Ce pattern est nécessaire pour [implémenter un trait externe sur un type extern](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types) (ce qui est interdit en Rust). Ainsi, il ne nous aurait pas été possible d'implémenter +le trait `Display` (qui permet d'afficher une instance du type), `Default` (qui +permet de créer une instance par défaut), ou `PartialEq` (qui permet de vérifier +l'égalité de deux instance du type) directement pour le type `Option<T>`, car +`Display`, `Default`, et `PartialEq` sont des traits externes tout comme le type +`Option<T>`. Pour des types externes l'implémentation de traits externes +est interdite (c'est la *orphan rule*). Cela interdit de "casser" un code externe +en autorisant de multiples implémentations du même trait pour le même type. +Ainsi `SomethingOrNothing<T>` nous permet d'implémenter ces trois traits +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:newtype_display}} +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:newtype_default}} +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:newtype_partialeq}} +``` +Nous aimerions attirer votre attention sur une particularité du *pattern matching* +ici. On voit que nous pouvons faire un match sur des types *imbriqués* +comme pour `SomethingOrNothing(Some(val))`, on va déstructurer les types énumérés +jusqu'à obtenir `val` qui est la valeur qui nous intéresse. + +Une deuxième utilité du pattern NewType est qu'elle permet de limiter +les fonctionnalités d'un type car cela nécessite de réimplémenter +les méthodes qui lui sont propres. Dans notre cas, seule la méthode `unwrap()` +de `Option<T>` nous intéresse (cette fonction retourne la valeur encapsulée dans +la variante `Some()` ou panique si on a un `None`). Ainsi, on n'implémente +que celle-là +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:newtype_unwrap}} +``` +On peut noter qu'on a à faire à une `struct` avec des membres anonymes. +Ainsi, les membres peuvent être accédés comme pour les tuples et comme il n'y +en a qu'un dans un `SomethingOrNothing<T>`, on y accède avec le sélecteur `self.0`. + +### Les lifetimes + +Dans cette section nous discutons l'utilisation de l'annotation des lifetimes +dans différents cas: les structures, les méthodes, les traits, et les fonctions. + +#### Les structures + +Dans notre programme, nous utiliserons le type `CustomInt` qui est une représentation d'un entier +qui peut avoir une taille arbitraire (dans les limites de la mémoire de la machine). +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/custom_int.rs:custom_int}} +``` +Un tel entier est représenté par un signe `i8` (qui peut valoir `+1` ou `-1`), et un +`Vec<u8>`, un tableau dynamique de `u8` (les valeurs admissibles vont de `0` à `9`), +qui contient les chiffres du nombre stockés de droite à gauche: `[7,3,3,1]` +est le nombre `1337`. + +Ces nombres pouvant être gigantesques, nous voulons éviter de les dupliquer lorsque nous +les copions ou les manipulons. Une solution est de stocker uniquement +une référence vers les données, c'est-à-dire que `data` est de type `&Vec<u8>`. +On voit dans le code ci-dessus, qu'il est nécessaire d'annoter la durée de vie +de la référence avec `'a`. Si on omet l'annotation de durée de vie le +compilateur nous préviendra et nous oblige à en spécifier un +```console +error[E0106]: missing lifetime specifier + --> src/custom_int.rs:13:11 + | +13 | data: &Vec<u8>, + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +9 ~ pub struct CustomInt<'a> { +10 | /// The data contains the unsigned integers that are read from right to left +11 | /// The number 1337 is stored as vec![7, 3, 3, 1]. Each number must be in the range [0,9] +12 | /// and no trailing 0s are allowed. +13 ~ data: &'a Vec<u8>, +``` +Il est en effet impossible pour le compilateur de savoir si la référence vivra +assez longtemps pour vivre plus longtemps qu'une instance de `CustomInt`. + +#### Les méthodes + +L'implémentation des méthodes requiert une annotation sous peine d'erreurs +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/custom_int.rs:custom_int_impl}} +``` +Il en va de même avec la fonction associée, `try_new()` +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/custom_int.rs:try_new}} +``` +qui nécessite +une annotation dans la définition du type de `data`. En effet, l'annotation +permet de dire au compilateur que `data`, l'argument de `try_new()`, vit suffisamment +longtemps pour permettre la création d'une instance de `CustomInt`. + +#### Les traits + +Il y a plusieurs traits qui sont implémentés pour `CustomInt<'a>`.: `PartialEq`, `Display`, et `Minumum`. Pour `PartialEq` et `Display`, il suffit de renseigner +l'annotation `'a` à l'instruction `impl` comme pour un paramètre générique, voir +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/custom_int.rs:partialeq}} +{{#include ../../codes/rust_lang/lifetimes/src/custom_int.rs:display}} +``` +Il est possible d'écrire la même chose, en omettant l'annotation `'a` +et en la remplaçant par `'_` pour simplifier la notation +```rust,ignore +impl PartialEq for CustomInt<'_> +``` +Comme l'annotation n'est utilisée nulle par, Rust offre ce sucre syntaxique +pour éviter d'écrire trop d'annotations. + +Pour le trait `Minimum` les choses se compliquent un peu. +Pour éviter le copies/clones, on a fait le choix de +n'utiliser que des références dans les arguments, comme dans le type de retour +du calcul du minimum de deux valeurs +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/minimum.rs:minimum}} +``` +Ainsi, comme il y deux références en argument et une référence en sortie, +il est nécessaire d'annoter les références, car sinon le compilateur ne sait pas +avec que durée de vie annoter la sortie (les 2 sont possibles). Ainsi nous annotons +le trait avec la durée de vie `'a`, puis cette durée de vie est utilisée pour toutes +les références dans la fonction `min()`. Ainsi toutes les références ont la même +durée de vie que celle annotée dans le trait. + +Il y a trois implémentation de ce trait: la première est pour les `i32`, la seconde pour `SomthingOrNothing<T>`, et finalement pour `CustomInt`. + +1. Pour l'implémentation +pour les `i32` +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/minimum.rs:min}} +``` +l'implémentation est triviale, il y a uniquement besoin de reprendre +l'annotation pour l'implémentation dans les références en argument +de la fonction et dans le retour de la fonction. En effet, comme les +deux arguments peuvent être retournés, il est nécessaire de préciser +au compilateur que la durée de vie sera la même, sinon il met automatiquement +une durée de vie à chaque argument et reprend celle à `&self` comme la durée +de vie de la sortie. En d'autres termes, sans annotations, on aurait +```rust,ignore +fn min(&self, rhs: &Self) -> &Self +``` +qui serait converti automatiquement par le compilateur en +```rust,ignore +fn min(&'a self, rhs: &'b Self) -> &'a Self { + if self < rhs { + self + } else { + rhs + } +} +``` +et la durée de vie `'a` du retour est pas compatible avec la durée de vie +qui serait retournée au moment de retourner `rhs` (qui est `'b`). Ce qui entraînerait une erreur de compilation (on aime pas ça les erreurs nous). +2. Pour l'implémentation pour `SomethingOrNothing<T>`, nous avons un paramètre générique. +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:min}} +``` +Ainsi nous constatons, que la ligne correspondant à la déclaration +de l'implémentation du trait `Minimum` nécessite la déclaration de la durée de vie `'a`, ainsi que du type générique `T`. On voit dans l'implémentation +de la fonction `min()`, que nous faisons appel à `min()` sur le type `T`, +et que donc celui-ci doit implémenter le trait `Minimum` (tout comme le trait `PartialEq`). +On doit ainsi répercuter la durée de vie sur tous les `Minimum` présents sur la ligne `impl` +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:impl_min}} +``` +Nous ne discutons pas l'implémentation à proprement parler qui est assez raisonnable +pour trouver le minimum de deux valeur encapsulées dans un `NewType`. +3. Finalement, on a l'implémentation pour `CustomInt` qui n'a rien de vraiment nouveau +par rapport aux implémentation précédentes (on réutilise l'annotation `'a` dans `min()` directement), à part la complexité monumentale de la fonction (elle fait plein de lignes) +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/custom_int.rs:min}} +``` +En effet, on doit faire attention au signe, à la longueur de notre `CustomInt` et +à plusieurs autres joyeusetés. Ici, on peut utiliser le trait `Ord` +(la fonction `cmp()`) pour faire les comparaisons entre le signe et les digits de +nos nombres. Le trait `Ord` représente les opérateurs `<, >, =, <=, >=`, via la fonction `cmp()` qui retourne trois types correspondants +```rust,ignore +Ordering::Less +Ordering::Greater +Ordering::Equal +``` +L'utilisation +d'un type énuméré pour gérer chacun des cas peut sembler verbeux et complexe. Cependant, +il permet de garantir à la *compilation* qu'on a pas oublié de traiter un cas par accident. Et ça, ça n'a pas de prix. + +#### Les fonctions + +Finalement, on utilise les lifetimes dans une fonction qui permet +le calcul du minimum dans un tableau et retourne un `SomethingOrNothing<&T>` contenant une référence vers l'élément le plus petit. + +Cette fonction est générique avec le paramètre `T` et prend en argument une référence qui doivent être annotée. + +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:find_min}} +``` +La fonction `find_min()` prend en argument un *slice* de type générique `T` qui doit implémenter `Minimum`. Comme le type de retour est `SomethingOrNothing<&T>`, donc on +encapsule une référence vers la valeur minimale, il est nécessaire d'annoter +les durées de vies car sinon elles auraient deux valeur différentes ce qui poserait problème au compilateur (car elles doivent être les mêmes). + +L'implémentation de cette fonction, n'est pas très complexe, mais est très intéressante. En premier lieu, pour des question de généricité de l'implémentation +nous passons en argument un *slice* de `T`: cette façon de procéder permet +d'avoir un argument qui serait une référence vers un tableau statique ou vers un `Vec<T>` sans changer l'implémentation. De plus, nous utilisons ici un itérateur +sur le tableau est faisons un `fold()` sur cet itérateur. Le `fold()` prend en argument +un élément neutre (quel est la valeur initiale stockée dans le `fold()`). Ici c'est +```rust,ignore +SomethingOrNothing::default() +``` +puis une fonction anonyme prenant deux arguments +```rust,ignore +|res, x| {} +``` +où la valeur retournée par cette fonction écrase la valeur de `res` à chaque `next()` +de l'itérateur et qui doit avoir le même type que l'élément neutre, et où `x` est la valeur courante de l'itérateur. Ici, le type de `res` est `SomethingOrNothing<&T>` et +le type de `x` est `&T`. La fonction anonyme +```rust,ignore +let r = match res { + SomethingOrNothing(None) => x, + SomethingOrNothing(Some(r)) => r.min(x), +}; +SomethingOrNothing::new(r) +``` +calcule le minimum entre la valeur actuelle stockée dans `res` et `x` en utilisant +la fonction `min()` ce qui implique que `T` doit implémenter `Minimum`. + +### En pratique + +Dans la fonction `main()` de notre programme +```rust,ignore +{{#include ../../codes/rust_lang/lifetimes/src/something_or_nothing.rs:find_min}} +``` +on crée un tableau de `CustomInt` qui sont créés à partir de références +sur les tableau `v1`, `v2`, etc. qui vivrons ainsi jusqu'à la fin de notre +programme et qui seront promenées sans qu'on ait besoin de les copier à aucun moment. +Les liens entre les durées de vie des références que nous nous sommes efforcés d'annoter dan tout au long ce code sont vérifiées par le compilateur qui +vérifie qu'elles sont toutes valides à la compilation. \ No newline at end of file diff --git a/codes/rust_lang/collections/Cargo.toml b/codes/rust_lang/collections/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..50a0d49c74afd47cb41267f168a32f4e80c1b20e --- /dev/null +++ b/codes/rust_lang/collections/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "collections" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.5" \ No newline at end of file diff --git a/codes/rust_lang/collections/src/io.rs b/codes/rust_lang/collections/src/io.rs new file mode 100644 index 0000000000000000000000000000000000000000..cf0f45476dd09bc12fbbf4a22843e293f86c6ae4 --- /dev/null +++ b/codes/rust_lang/collections/src/io.rs @@ -0,0 +1,68 @@ +//! Contains functions to interact with the user, either +//! by reading inputs from the terminal, either by writing values +//! in it. +use rand::Rng; + +// ANCHOR: read_command_line +/// Poorly emulates the parsing of a command line. +pub fn read_command_line(len: usize) -> Vec<i32> { + let mut rng = rand::thread_rng(); + // ANCHOR: vec_new + let mut v: Vec<i32> = Vec::new(); + // ANCHOR_END: vec_new + // ANCHOR: vec_for + for _i in 0..len { + // ANCHOR: vec_push + v.push(rng.gen()); + // ANCHOR: vec_push + } + // ANCHOR_END: vec_for + v +} +// ANCHOR_END: read_command_line + +// ANCHOR: read_command_line_str +/// Poorly emulates the parsing of a command line. +pub fn read_command_line_str() -> Result<Vec<i32>, String> { + // ANCHOR: from + let mut s = String::from("20 10 48 58 29 0 58 -10 39 5485 394"); + // ANCHOR_END: from + // ANCHOR: push_str + s.push_str(" -100"); + // ANCHOR_END: push_str + // ANCHOR: push_char + s.push(' '); + s.push('1'); + s.push('2'); + // ANCHOR_END: push_char + // ANCHOR: split + let s: Vec<&str> = s.split_ascii_whitespace().collect(); + // ANCHOR_END: split + + // ANCHOR: string_for + let mut v = Vec::new(); + for i in 0..s.len() { + v.push( + // ANCHOR: conversion + s.get(i) + .ok_or(String::from("Unable to index"))? + .parse() + .map_err(|_| format!("Unable to parse {}", s[i]))?, + // ANCHOR_END: conversion + ); + } + // ANCHOR_END: string_for + Ok(v) +} +// ANCHOR_END: read_command_line_str + +/// Prints all the elements of the `tab`. +/// Tab is borrowed here +// ANCHOR: print_tab +pub fn print_tab(tab: &[i32]) { + for t in tab { + print!("{} ", t); + } + println!(); +} +// ANCHOR_END: print_tab diff --git a/codes/rust_lang/collections/src/lib.rs b/codes/rust_lang/collections/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..0b02fce36d04c0f048032fd5600d5df3ea7be30d --- /dev/null +++ b/codes/rust_lang/collections/src/lib.rs @@ -0,0 +1,80 @@ +//! This is an example of Rust crate comments (or inner comments). +//! They will be rendered in the front page of your (crate) library. +//! +//! # How to generate the documentation +//! +//! In this program we wrote an algorithm that computes the minimum of +//! a sequence of integers. +//! +//! To create the documentation run the command +//! ```bash +//! cargo doc +//! ``` +//! The obtain documentation can be found in the `target/doc/collections/index.html` directory +//! +//! To view the documentation type +//! ```bash +//! cargo doc --open +//! ``` +//! which will open the browser and show you the documentation. +//! +//! The documentation supports the CommonMarkdown syntax. +//! +//! Below we will use the `///` comments that will comment the code directly below. +//! We can also sue `//` but they will not be rendered. +//! All the lines written here could be enclosed in `/*! ... */` instead of being prefixed by `//!`. +//! +//! For more informations about writing documentation [follow that link](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html). +//! +//! # Tooling +//! +//! Also Rust comes with great tooling. +//! - [Clippy](https://doc.rust-lang.org/stable/clippy/): The officiel Rust linter. +//! - [Rustfmt](https://github.com/rust-lang/rustfmt): The official Rust code formatter. + +pub mod io; +pub mod minimum; +pub mod something_or_nothing; + +#[test] +fn test_creation() { + use something_or_nothing::SomethingOrNothing; + + let n1: SomethingOrNothing<i32> = SomethingOrNothing::default(); + assert!(n1 == SomethingOrNothing::Nothing); + let n2: SomethingOrNothing<i32> = SomethingOrNothing::Something(1); + assert!(n2 == SomethingOrNothing::Something(1)); +} + +#[cfg(test)] +mod tests { + use crate::minimum::Minimum; + use crate::something_or_nothing::{find_min, SomethingOrNothing}; + + #[test] + #[should_panic] + fn test_failure_creation() { + let n2: SomethingOrNothing<i32> = SomethingOrNothing::Something(1); + assert!(n2 == SomethingOrNothing::Nothing); + assert!(n2 == SomethingOrNothing::Something(2)); + } + + #[test] + fn test_min() { + let a = [1, 5, -1, 2, 0, 10, 11, 0, 3]; + let min = find_min(&a); + assert!(min == SomethingOrNothing::Something(-1)); + } + + #[test] + fn test_min_something_or_nothing() { + let x = SomethingOrNothing::Something(5i32); + let y = SomethingOrNothing::Something(10i32); + let z = SomethingOrNothing::Nothing; + assert!(x.min(y) == x); + assert!(y.min(x) == x); + assert!(z.min(y) == y); + assert!(y.min(z) == y); + assert!(z.min(z) == z); + } +} diff --git a/codes/rust_lang/collections/src/main.rs b/codes/rust_lang/collections/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..9f9263a1e5a55d0d5daece7dcdec9159f0d9da5a --- /dev/null +++ b/codes/rust_lang/collections/src/main.rs @@ -0,0 +1,35 @@ +use collections::io; +use collections::something_or_nothing::find_min; + +fn main() -> Result<(), String> { + //ANCHOR: vec + let tab: Vec<i32> = io::read_command_line(10usize); + println!("Among the Somethings in the list:"); + io::print_tab(&tab); + let min = find_min(&tab); + min.print(); + //ANCHOR_END: vec + + let tab = io::read_command_line_str()?; + println!("Among the Somethings in the list:"); + io::print_tab(&tab); + let min = find_min(&tab); + min.print(); + + //ANCHOR: ref + println!("Among the Somethings in the list:"); + io::print_tab(&tab[1..9]); + let min = find_min(&tab[1..9]); + min.print(); + //ANCHOR_END: ref + + //ANCHOR: tab + let tab = [1, 2, 3, 4, 5, 6]; + println!("Among the Somethings in the list:"); + io::print_tab(&tab); + let min = find_min(&tab); + min.print(); + //ANCHOR_END: tab + + Ok(()) +} diff --git a/codes/rust_lang/collections/src/minimum.rs b/codes/rust_lang/collections/src/minimum.rs new file mode 100644 index 0000000000000000000000000000000000000000..a4233159e669e7e910ccf23634b47f599e4d583c --- /dev/null +++ b/codes/rust_lang/collections/src/minimum.rs @@ -0,0 +1,43 @@ +//! Contains a generic trait implementation for computing the minimum between two +//! values. It is the equivalent of the `<` operator. +//! +//! # Examples +//! +//! For integers this would look like +//! +//! ``` +//! # use collections::minimum::Minimum; +//! let one = 1; +//! let two = 2; +//! assert!(Minimum::min(one, two) == one); +//! ``` + +/// The [Minimum] trait computes the minimum value between two values of a type +pub trait Minimum: Copy { + fn min(self, rhs: Self) -> Self; +} + +impl Minimum for i32 { + fn min(self, rhs: Self) -> Self { + if self < rhs { + self + } else { + rhs + } + } +} + +#[cfg(test)] +mod tests { + use crate::minimum::Minimum; + + #[test] + fn test_min_i32() { + let x = 5; + let y = 10; + assert_eq!(Minimum::min(x, y), x); + assert_eq!(Minimum::min(y, x), x); + assert_eq!(Minimum::min(x, x), x); + assert_eq!(Minimum::min(y, y), y); + } +} diff --git a/codes/rust_lang/collections/src/something_or_nothing.rs b/codes/rust_lang/collections/src/something_or_nothing.rs new file mode 100644 index 0000000000000000000000000000000000000000..92fbd9423024d176aa6e9d0f8cc7f11705465135 --- /dev/null +++ b/codes/rust_lang/collections/src/something_or_nothing.rs @@ -0,0 +1,104 @@ +//! Contains the core logic of the library, allowing to tore generic values +//! (or their absence) and manipulate them. + +use crate::minimum::Minimum; + +/// An generic enumerated type that has two variants. +/// +/// - Nothing +/// - Something +#[derive(Clone, Copy)] +pub enum SomethingOrNothing<T> { + /// A [SomethingOrNothing::Nothing] + Nothing, + /// A [SomethingOrNothing::Something] encapsulating a T + Something(T), +} + +impl<T: std::fmt::Display> SomethingOrNothing<T> { + /// A static function that prints the content of a SomethingOrNothing. + pub fn print(&self) { + match self { + SomethingOrNothing::Nothing => println!("Nothing."), + SomethingOrNothing::Something(val) => println!("Something is: {}", val), + } + } +} + +/// Implementation of the [Default] trait that creates a [SomethingOrNothing] +/// that is a `Nothing` variant. +/// +/// # Example +/// +/// ``` +/// # use collections::something_or_nothing::SomethingOrNothing; +/// # fn main() { +/// let def: SomethingOrNothing<i32> = SomethingOrNothing::default(); +/// assert!(def == SomethingOrNothing::Nothing); +/// # } +/// ``` +impl<T> Default for SomethingOrNothing<T> { + /// By Default a [SomethingOrNothing] is a nothing. + fn default() -> Self { + SomethingOrNothing::Nothing + } +} + +/// Implementation of the [PartialEq] trait that is useful for tests. +impl<T: PartialEq> PartialEq for SomethingOrNothing<T> { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (SomethingOrNothing::Nothing, SomethingOrNothing::Nothing) => true, + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Something(rhs)) => { + *lhs == *rhs + } + _ => false, + } + } +} + +/// Implementation of the [Minimum] trait used for comparing values +/// in this crate. +impl<T: Minimum> Minimum for SomethingOrNothing<T> { + fn min(self, rhs: Self) -> Self { + match (self, rhs) { + (SomethingOrNothing::Nothing, SomethingOrNothing::Nothing) => { + SomethingOrNothing::Nothing + } + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Something(rhs)) => { + SomethingOrNothing::Something(lhs.min(rhs)) + } + (SomethingOrNothing::Nothing, SomethingOrNothing::Something(rhs)) => { + SomethingOrNothing::Something(rhs) + } + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Nothing) => { + SomethingOrNothing::Something(lhs) + } + } + } +} + +/// Computes the minimum of an Array of a type T which implements the [Minimum] trait. +/// Returns a [SomethingOrNothing::Something] containing the the minimum value +/// or [SomethingOrNothing::Nothing] if no minimum value was found. +/// +/// # Example +/// +/// ``` +/// # use collections::something_or_nothing::{SomethingOrNothing, find_min}; +/// # fn main() { +/// let tab = vec![10, 32, 12, 43, 52, 53, 83, 2, 9]; +/// let min = find_min(&tab); +/// assert!(min == SomethingOrNothing::Something(2)); +/// # } +/// ``` +// ANCHOR: find_min +pub fn find_min<T: Minimum>(tab: &[T]) -> SomethingOrNothing<T> { + let mut minimum = SomethingOrNothing::Nothing; + // Here is T is Copyable. Which means that t is not moved in the loop + for t in tab { + minimum = minimum.min(SomethingOrNothing::Something(*t)); + } + minimum +} +// ANCHOR_END: find_min diff --git a/codes/rust_lang/lifetimes/Cargo.toml b/codes/rust_lang/lifetimes/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..bc3f2629be97e53631ee5186294757a3e8844c1d --- /dev/null +++ b/codes/rust_lang/lifetimes/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "lifetimes" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/codes/rust_lang/lifetimes/src/custom_int.rs b/codes/rust_lang/lifetimes/src/custom_int.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a4c97e9a4f5214e06d072bc0d755f5d5e3f564d --- /dev/null +++ b/codes/rust_lang/lifetimes/src/custom_int.rs @@ -0,0 +1,198 @@ +use std::cmp::Ordering; + +use crate::minimum::Minimum; + +/// Larger ints based on a [Vec] of [u8] to repensent arbitrary lengthy numbers. +/// The number has a sign as well. +// ANCHOR: custom_int +#[derive(Debug)] +pub struct CustomInt<'a> { + /// The data contains the unsigned integers that are read from right to left + /// The number 1337 is stored as vec![7, 3, 3, 1]. Each number must be in the range [0,9] + /// and no trailing 0s are allowed. + data: &'a Vec<u8>, + /// Contains the sign of the number +/-1; + sign: i8, +} +// ANCHOR_END: custom_int + +// ANCHOR: custom_int_impl +impl<'a> CustomInt<'a> +// ANCHOR_END: custom_int_impl +{ + /// Tries to create a new [CustomInt]. If the number is valid it returns + /// an Ok(CustomInt) an Error otherwise. + /// + /// # Examples + /// + /// ``` + /// use lifetimes::custom_int::CustomInt; + /// let v1 = vec![1, 2, 3, 4]; + /// let num = CustomInt::try_new(&v1, 1); + /// assert!(num.is_ok()); + /// let num = CustomInt::try_new(&v1, -1); + /// assert!(num.is_ok()); + /// let num = CustomInt::try_new(&v1, 10); + /// assert!(num.is_err()); + /// let num = CustomInt::try_new(&v1, -10); + /// assert!(num.is_err()); + /// let v1 = vec![]; + /// let num = CustomInt::try_new(&v1, -1); + /// assert!(num.is_err()); + /// ``` + /// + // ANCHOR: try_new + pub fn try_new(data: &'a Vec<u8>, sign: i8) -> Result<Self, String> { + if data.is_empty() { + Err(String::from("Data is empty.")) + } else if sign == 1 || sign == -1 { + Ok(CustomInt { data, sign }) + } else { + Err(String::from("Invalid sign.")) + } + } + // ANCHOR_END: try_new +} + +// ANCHOR: minimum +impl<'a> Minimum<'a> for CustomInt<'a> +// ANCHOR_END: minimum +{ + // ANCHOR: min + fn min(&'a self, rhs: &'a Self) -> &'a Self { + match self.sign.cmp(&rhs.sign) { + Ordering::Less => return self, + Ordering::Greater => return rhs, + Ordering::Equal => match self.data.len().cmp(&rhs.data.len()) { + Ordering::Less => { + if self.sign == 1 { + return self; + } else { + return rhs; + } + } + Ordering::Greater => { + if self.sign == 1 { + return rhs; + } else { + return self; + } + } + Ordering::Equal => { + for (l, r) in self.data.iter().rev().zip(rhs.data.iter().rev()) { + let ls = (*l as i8) * self.sign; + let rs = (*r as i8) * self.sign; + match ls.cmp(&rs) { + Ordering::Less => return self, + Ordering::Greater => return rhs, + Ordering::Equal => {} + } + } + } + }, + } + self + } + // ANCHOR_END: min +} + +// ANCHOR: partialeq +impl<'a> PartialEq for CustomInt<'a> +// ANCHOR_END: partialeq +{ + fn eq(&self, other: &Self) -> bool { + if self.sign == other.sign && self.data.len() == other.data.len() { + self.data + .iter() + .zip(other.data.iter()) + .try_fold(true, |_, (l, r)| if *l == *r { Some(true) } else { None }) + .is_some() + } else { + false + } + } +} + +// ANCHOR: display +impl<'a> std::fmt::Display for CustomInt<'a> +// ANCHOR_END: display +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // This could be replaced by an `?` + if self.sign == -1 { + write!(f, "-")?; + } + + // This could be replaced by an `?` + let res = self + .data + .iter() + .rev() + .try_fold((), |_, t| write!(f, "{}", t)); + res + } +} + +#[cfg(test)] +mod tests { + use crate::custom_int::CustomInt; + use crate::minimum::Minimum; + use crate::something_or_nothing::find_min; + + #[test] + fn test_creation() { + let v1 = vec![1, 2, 3, 4]; + CustomInt::try_new(&v1, 1).unwrap(); + CustomInt::try_new(&v1, -1).unwrap(); + } + + #[test] + #[should_panic] + fn test_failure_creation_sign() { + let v1 = vec![1, 2, 3, 4]; + CustomInt::try_new(&v1, 10).unwrap(); + } + + #[test] + #[should_panic] + fn test_failure_creation_sign2() { + let v1 = vec![1, 2, 3, 4]; + CustomInt::try_new(&v1, 0).unwrap(); + } + + #[test] + #[should_panic] + fn test_failure_creation_data() { + let v1 = vec![]; + CustomInt::try_new(&v1, 1).unwrap(); + } + + #[test] + fn test_min() { + let mut v = Vec::new(); + let v1 = vec![1, 2, 3, 4]; + let v2 = vec![1, 2, 3]; + let lhs = CustomInt::try_new(&v1, 1).unwrap(); + let rhs = CustomInt::try_new(&v2, 1).unwrap(); + assert!(rhs == *lhs.min(&rhs)); + v.push(lhs); + v.push(rhs); + let lhs = CustomInt::try_new(&v1, -1).unwrap(); + let rhs = CustomInt::try_new(&v2, -1).unwrap(); + assert!(lhs == *lhs.min(&rhs)); + v.push(lhs); + v.push(rhs); + let v1 = vec![1, 2, 3, 4]; + let v2 = vec![1, 2, 5, 4]; + let lhs = CustomInt::try_new(&v1, -1).unwrap(); + let rhs = CustomInt::try_new(&v2, -1).unwrap(); + assert!(rhs == *lhs.min(&rhs)); + v.push(lhs); + v.push(rhs); + let lhs = CustomInt::try_new(&v1, 1).unwrap(); + let rhs = CustomInt::try_new(&v2, 1).unwrap(); + assert!(lhs == *lhs.min(&rhs)); + let min = find_min(&v); + assert_eq!(*min.unwrap(), CustomInt::try_new(&v2, -1).unwrap()); + } +} diff --git a/codes/rust_lang/lifetimes/src/io.rs b/codes/rust_lang/lifetimes/src/io.rs new file mode 100644 index 0000000000000000000000000000000000000000..0f8342e83a34704aa46d882808f59172993f77f6 --- /dev/null +++ b/codes/rust_lang/lifetimes/src/io.rs @@ -0,0 +1,19 @@ +use crate::custom_int::CustomInt; + +/// Prints all the elements of the `tab`. +/// Tab is borrowed here +pub fn print_tab(tab: &Vec<i32>) { + for t in tab { + print!("{} ", t); + } + println!(); +} + +/// Prints all the elements of the `tab`. +/// Tab is borrowed here +pub fn print_tab_custom_int(tab: &Vec<CustomInt>) { + for i in tab { + println!("{i} "); + } + println!(); +} diff --git a/codes/rust_lang/lifetimes/src/lib.rs b/codes/rust_lang/lifetimes/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..239d1374768cf5b5ca2ba754e5ad49e97d720f8d --- /dev/null +++ b/codes/rust_lang/lifetimes/src/lib.rs @@ -0,0 +1,67 @@ +/*! +lifetimes illustrates the use of [Vec] and the Error Handling with [Option] and [Result]. +It also showcases struct enums. +*/ + +pub mod custom_int; +pub mod io; +mod minimum; +pub mod something_or_nothing; + +#[cfg(test)] +mod tests { + use crate::minimum::Minimum; + use crate::something_or_nothing::{find_min, SomethingOrNothing}; + + #[test] + fn test_creation() { + let n1: SomethingOrNothing<i32> = SomethingOrNothing::default(); + assert!(n1 == SomethingOrNothing::default()); + let n2: SomethingOrNothing<i32> = SomethingOrNothing::new(1); + assert!(n2 == SomethingOrNothing::new(1)); + } + + #[test] + #[should_panic] + fn test_failure_creation() { + let n2: SomethingOrNothing<i32> = SomethingOrNothing::new(1); + assert!(n2 == SomethingOrNothing::default()); + assert!(n2 == SomethingOrNothing::new(2)); + } + + #[test] + fn test_min() { + let a = vec![1, 5, -1, 2, 0, 10, 11, 0, 3]; + let min = find_min(&a); + assert!(*min.unwrap() == -1); + } + + #[test] + fn test_min_empty() { + let a: Vec<i32> = vec![]; + let min = find_min(&a); + assert!(min == SomethingOrNothing::default()); + } + + #[test] + fn test_min_i32() { + let x = 5; + let y = 10; + assert_eq!(*Minimum::min(&x, &y), x); + assert_eq!(*Minimum::min(&y, &x), x); + assert_eq!(*Minimum::min(&x, &x), x); + assert_eq!(*Minimum::min(&y, &y), y); + } + + #[test] + fn test_min_something_or_nothing() { + let x = SomethingOrNothing::new(5i32); + let y = SomethingOrNothing::new(10i32); + let z = SomethingOrNothing::default(); + assert!(*x.min(&y) == x); + assert!(*y.min(&x) == x); + assert!(*z.min(&y) == y); + assert!(*y.min(&z) == y); + assert!(*z.min(&z) == z); + } +} diff --git a/codes/rust_lang/lifetimes/src/main.rs b/codes/rust_lang/lifetimes/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..4ed86dcc78a35eab943f8dedb97c8f33130d604c --- /dev/null +++ b/codes/rust_lang/lifetimes/src/main.rs @@ -0,0 +1,32 @@ +use lifetimes::custom_int::CustomInt; +use lifetimes::io; +use lifetimes::something_or_nothing::find_min; + +// ANCHOR: main +fn main() -> Result<(), String> { + let v1 = vec![1, 3, 6, 9]; + let v2 = vec![2, 4, 2, 1]; + let v3 = vec![7, 4, 5, 3]; + let v4 = vec![4, 1, 1, 1]; + let v5 = vec![2, 5, 1, 8]; + let v6 = vec![5, 1, 5, 2]; + let v7 = vec![7, 6, 6, 7]; + let v8 = vec![8, 2, 2, 2]; + let lhs = vec![ + CustomInt::try_new(&v1, 1)?, + CustomInt::try_new(&v2, -1)?, + CustomInt::try_new(&v3, 1)?, + CustomInt::try_new(&v4, -1)?, + CustomInt::try_new(&v5, 1)?, + CustomInt::try_new(&v6, 1)?, + CustomInt::try_new(&v7, 1)?, + CustomInt::try_new(&v8, 1)?, + ]; + + println!("Among the custom ints in the list:"); + io::print_tab_custom_int(&lhs); + let min = find_min(&lhs); + println!("The minimum is {min}"); + Ok(()) +} +// ANCHOR_END: main diff --git a/codes/rust_lang/lifetimes/src/minimum.rs b/codes/rust_lang/lifetimes/src/minimum.rs new file mode 100644 index 0000000000000000000000000000000000000000..fdc92933bbb6f8f9bb6acf691ef476441f7badd4 --- /dev/null +++ b/codes/rust_lang/lifetimes/src/minimum.rs @@ -0,0 +1,17 @@ +// ANCHOR: minimum +pub trait Minimum<'a> { + fn min(&'a self, rhs: &'a Self) -> &'a Self; +} +// ANCHOR_END: minimum + +// ANCHOR: min +impl<'a> Minimum<'a> for i32 { + fn min(&'a self, rhs: &'a Self) -> &'a Self { + if self < rhs { + self + } else { + rhs + } + } +} +// ANCHOR_END: min diff --git a/codes/rust_lang/lifetimes/src/something_or_nothing.rs b/codes/rust_lang/lifetimes/src/something_or_nothing.rs new file mode 100644 index 0000000000000000000000000000000000000000..8ce39362c41bbadf69ed2ffe3a01e9515a508919 --- /dev/null +++ b/codes/rust_lang/lifetimes/src/something_or_nothing.rs @@ -0,0 +1,145 @@ +use std::fmt::Display; + +use crate::minimum::Minimum; + +/// An generic enumerated type that encapsulates and Option<T>. +// ANCHOR: newtype +#[derive(Debug)] +pub struct SomethingOrNothing<T>(Option<T>); +// ANCHOR_END: newtype + +impl<T> SomethingOrNothing<T> { + pub fn new(val: T) -> Self { + SomethingOrNothing(Some(val)) + } + + // ANCHOR: newtype_unwrap + pub fn unwrap(self) -> T { + self.0.unwrap() + } + // ANCHOR_END: newtype_unwrap +} + +// ANCHOR: newtype_display +impl<T: Display> std::fmt::Display for SomethingOrNothing<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + SomethingOrNothing(None) => write!(f, "Nothing.")?, + SomethingOrNothing(Some(val)) => write!(f, "Something is: {}", val)?, + } + Ok(()) + } +} +// ANCHOR_END: newtype_display + +// ANCHOR: newtype_default +impl<T> Default for SomethingOrNothing<T> { + /// By Default a [SomethingOrNothing] is a nothing. + fn default() -> Self { + SomethingOrNothing(None) + } +} +// ANCHOR_END: newtype_default + +// ANCHOR: newtype_partialeq +impl<T: PartialEq> PartialEq for SomethingOrNothing<T> { + fn eq(&self, other: &Self) -> bool { + match (&self, &other) { + (SomethingOrNothing(None), SomethingOrNothing(None)) => true, + (SomethingOrNothing(Some(lhs)), SomethingOrNothing(Some(rhs))) => lhs == rhs, + _ => false, + } + } +} +// ANCHOR_END: newtype_partialeq + +// ANCHOR: min +// ANCHOR: impl_min +impl<'a, T: Minimum<'a> + PartialEq> Minimum<'a> for SomethingOrNothing<T> +// ANCHOR_END: impl_min +{ + fn min(&'a self, rhs: &'a Self) -> &'a Self { + match (self, rhs) { + (SomethingOrNothing(None), SomethingOrNothing(None)) => self, + (SomethingOrNothing(Some(l)), SomethingOrNothing(Some(r))) => { + if *l == *l.min(r) { + self + } else { + rhs + } + } + (SomethingOrNothing(None), SomethingOrNothing(Some(_))) => rhs, + (SomethingOrNothing(Some(_)), SomethingOrNothing(None)) => self, + } + } +} +// ANCHOR_END: min + +/// Computes the minimum of an Array of a type T which implements the [Minimum] trait. +/// Returns a [Something] containing the the minimum value +/// or [Nothing] if no minimum value was found. +/// +/// # Examples +/// +/// ``` +/// # use lifetimes::something_or_nothing::{SomethingOrNothing, find_min}; +/// # fn main() { +/// let tab = vec![10, 32, 12, 43, 52, 53, 83, 2, 9]; +/// let min = find_min(&tab); +/// assert!(*min.unwrap() == 2); +/// # } +/// ``` +/// +/// ``` +/// # use lifetimes::something_or_nothing::{SomethingOrNothing, find_min}; +/// # fn main() { +/// let tab: Vec<i32> = vec![]; +/// let min = find_min(&tab); +/// assert!(min == SomethingOrNothing::default()); +/// # } +/// ``` +// ANCHOR: find_min +pub fn find_min<'a, T: Minimum<'a>>(tab: &'a [T]) -> SomethingOrNothing<&'a T> { + // A very elegant fold applied on an iterator + tab.iter().fold(SomethingOrNothing::default(), |res, x| { + let r = match res { + SomethingOrNothing(None) => x, + SomethingOrNothing(Some(r)) => r.min(x), + }; + SomethingOrNothing::new(r) + }) +} +// ANCHOR_END: find_min + +/// Finds the minimum values contained in two slices and returns the reference +/// towards the slice that contains it. +/// +/// # Examples +/// +/// ``` +/// # use lifetimes::something_or_nothing::{SomethingOrNothing, vec_with_min}; +/// # fn main() { +/// let tab1 = vec![10, 32, 12, 43, 52, 53, 83, 2, 9]; +/// let tab2 = vec![10, 32, 12, 43, -2, 53, 83, 2, 9]; +/// +/// let min = vec_with_min(&tab1, &tab2).unwrap(); +/// assert!(min == &tab2); +/// # } +/// ``` +pub fn vec_with_min<'a, T: Minimum<'a> + PartialEq>( + lhs: &'a [T], + rhs: &'a [T], +) -> SomethingOrNothing<&'a [T]> { + match (find_min(lhs), find_min(rhs)) { + (SomethingOrNothing(None), SomethingOrNothing(None)) => SomethingOrNothing::default(), + (SomethingOrNothing(None), SomethingOrNothing(Some(_))) => SomethingOrNothing::new(rhs), + (SomethingOrNothing(Some(_)), SomethingOrNothing(None)) => SomethingOrNothing::new(lhs), + (SomethingOrNothing(Some(l)), SomethingOrNothing(Some(r))) => { + if *l == *l.min(r) { + SomethingOrNothing::new(lhs) + } else { + SomethingOrNothing::new(rhs) + } + } + } +} diff --git a/slides/src/SUMMARY.md b/slides/src/SUMMARY.md index 4c9d844f78e95c27c12bb90dd5991243f695e400..399cd777d11ee1d780ecfcfb6d2c34402f7ef7ca 100644 --- a/slides/src/SUMMARY.md +++ b/slides/src/SUMMARY.md @@ -20,6 +20,6 @@ - [Vecteurs](vec.md). - [Les Strings](string.md). <!-- - [Itérateurs](iterators.md). -- [Unsafe Rust](collections.md). -- [Lifetimes](lifetimes.md). --> +- [Unsafe Rust](collections.md). --> +- [Lifetimes](lifetimes.md). - [Unsafe](unsafe.md). diff --git a/slides/src/lifetimes.md b/slides/src/lifetimes.md new file mode 100644 index 0000000000000000000000000000000000000000..b1d7834c84e47ac4b8c935fa5c8a28ec43752480 --- /dev/null +++ b/slides/src/lifetimes.md @@ -0,0 +1,138 @@ +# Lifetimes + +## Ressources + +* [The Book](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) +* [The Rustonomicon](https://doc.rust-lang.org/nomicon/lifetimes.html) + +## Problématique + +```rust [2|3-6|7|] compile_fail +fn main() { + let r; + { + let x = 5; + r = &x; + } + println!("r: {}", r); +} +``` + +## Durée de vie: annotation + +```rust [] compile_fail +fn main() { + let r: &'a i32; // --------+-- 'a + { // | + let x: 'b i32 = 5; // -+--'b | + r = &'b x; // | | + } // -+ | + println!("r: {}", r); // --------+ // x vit plus +} +``` + +* Correction? + +## Inférence + +* Souvent la durée de vie est **inférée**, +* Comme pour les **types**, on doit préciser quand il y a un **doute**, +* Utile que pour la **compilation**: the famous *borrow checker*, +* Si vous devez annoter votre code, il se peut que vous fassiez un truc trop compliqué. + +## Exemple + +```rust [8-13|1,7|] compile_fail +fn longest(x: &str, y: &str) -> &str { + if x.len() > y.len() { + x + } else { + y + } +} // return lifetime is... x or y? +fn main() { + let string1 = String::from("abcd"); + let string2 = "xyz"; + let result = longest(string1.as_str(), string2); + println!("The longest string is {}", result); +} +``` + +## Correction + +```rust [1,7|] +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + if x.len() > y.len() { + x + } else { + y + } +} // all have the same lifetime +fn main() { + let string1 = String::from("abcd"); + let string2 = "xyz"; + let result = longest(string1.as_str(), string2); + println!("The longest string is {}", result); +} +``` + +## Annotation + +* Indique au compilateur les relations de durées de vie, +* Ne permet pas d'augmenter une durée de vie, +* Le compilateur vérifie que la durée de vie est **compatible** avec la durée de vie. + +```rust [] ignore +&T // référence +&'a T // référence avec durée de vie a +&'a mut T // référence mutable avec durée de vie a +``` + +## Dans une structure + +```rust [1-3|6-8|] +struct VecView<'a> { + view: &'a [i32] +} +fn main() { + let v = vec![1, 2, 3, 4, 5, 6]; + let vv = VecView { + view: &v[1..4], + }; + println!("view = {:?}", vv.view); +} +``` + +* Le compilateur vérifie que `VecView` ne vit pas plus longtemps que sa référence `view`. + +## Élision: fonction + +```rust +fn vecview(v: &[i32], min: usize, max: usize) -> &[i32] { + &v[min..max] +} +``` + +* Règle 1: Chaque référence en argument à sa durée de vie, +* Règle 2: Si unique durée de vie en argument, la sortie obtient la même + +## Élision: méthode + +```rust [1-3|4-9|] +struct VecView<'a> { + view: &'a [i32] +} +impl<'a> VecView<'a> { + fn show(&self, original: &[i32]) -> &[i32] { + println!("we view {:?}, from {:?}", self.view, original); + self.view + } +} +fn main() { + let v = vec![1, 2, 3, 4, 5, 6]; + let vv = VecView { view: &v[1..4] }; + vv.show(&v); +} +``` +* Règle 3: Si un argument est `&self` ou `&mut self`, la sortie a automatiquement la même durée de vie. +