diff --git a/tp3/README.md b/tp3/README.md index 674fbd6896fdc4355ab811800d303f497a4a3007..bdd457f7a5178f65a8fc3cad835c3fd7b55340e8 100644 --- a/tp3/README.md +++ b/tp3/README.md @@ -24,7 +24,11 @@ trait Decoder { fn decode(&self, message: &str) -> String; } ``` + ### 1.1.2. A first encoder + +On utilise une méthode pour transformer `message` de la bonne façon avant de retourner un `String` : + ```rs impl Encoder for ToUpper { fn encode(&self, message: &str) -> String { @@ -38,7 +42,11 @@ impl Encoder for ToLower { } } ``` + ### 1.1.3. Two traits for one type + +Pour encoder on parcours un à un les caractères de `message` en appliquant à chaque fois le shift, et inversement pour décoder : + ```rs struct Caesar { shift: u8, @@ -76,7 +84,11 @@ impl Decoder for Caesar { } } ``` + ### 1.1.4. Implement a trait for a foreign type + +Pour ajouter les fonctions `encode()` et `decode()` on a cette fois-ci implémenté le trait `CaesarExt` au type `str` : + ```rs trait CaesarExt { /// Encode self using a Caesar's cypher with the given shift @@ -98,6 +110,7 @@ impl CaesarExt for str { ``` ## 1.2. Implementing common traits ### 1.2.1. à 1.2.4. Clone/Copy/Debug/PartialEq + On peut gérer ces 4 cas simplement en utilisant `derive` : ```rs @@ -115,6 +128,9 @@ enum Instruction { ``` ### 1.2.5. Display + +On doit implémenter la méthode `fmt()` du trait `Display` : + ```rs impl std::fmt::Display for Instruction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -127,7 +143,11 @@ impl std::fmt::Display for Instruction { } } ``` + ### 1.2.6. FromStr + +On parcours `s` et on fabrique un `Instruction` en fonction de ce qui était contenu dedans : + ```rs #[derive(Debug, PartialEq)] enum InstructionError { @@ -178,6 +198,9 @@ impl std::str::FromStr for Instruction { } ``` ### 1.2.7. TryFrom<&[u8]> for Instruction + +On construit un `Instruction` simplement avec un match : + ```rs #[derive(Debug)] enum TryFromInstructionError { @@ -218,7 +241,11 @@ impl TryFrom<&[u8]> for Instruction { } } ``` + ### 1.2.8. From<Instruction> for [u8; 9] + +Comme pour la question précédente on parcours le paramètre, mais cette fois en reprenant la logique de construction d'un `Instruction` à partir d'un `[u8; 9]` mais dans l'autre sens : + ```rs impl From<Instruction> for [u8; 9] { fn from(inst: Instruction) -> Self { @@ -249,12 +276,20 @@ impl From<Instruction> for [u8; 9] { } } ``` + ### 1.2.9. Tests + +Normalement les tests passent : + ```shell cargo test ``` + # 2. Generics ## 2.1. Without trait bounds + +Pour chaque méthode on vérifie dans quel cas de `Either` l'on est et on retourne la bonne réponse : + ```rs enum Either<L, R> { Left(L), @@ -303,7 +338,11 @@ impl<L, R> Either<L, R> { } } ``` + ## 2.2. With trait bounds + +On utilise `where` pour préciser que les trait bounds. Pour encoder et décoder, on lit ce qui est sur le `reader` et pour chaque ligne on encode ou décode avec `encoder.encode()` ou `decoder.decode()`, puis on l'écrit sur le `writer` : + ```rs use std::io::{BufRead, BufReader, BufWriter, Read, Write}; @@ -355,8 +394,11 @@ where } } ``` + # 3. Long exercise proposal +On défini un trait `Game` générique : + ```rs use std::io::{BufRead, BufReader, BufWriter, Read, Write}; @@ -372,6 +414,8 @@ trait Game { } ``` +On implémente Tic Tac Toe : + ```rs #[derive(Clone, Copy, Debug, PartialEq)] enum TicTacToePlayer { @@ -484,6 +528,8 @@ impl Game for TicTacToe { } ``` +On implémente aussi le jeu de Nim : + ```rs #[derive(Clone, Copy, Debug, PartialEq)] enum NimGamePlayer { @@ -541,6 +587,8 @@ impl Game for NimGame { } ``` +Grâce au fait que `Game` est générique, on peut avoir une seule méthode `play_game()` pour les deux jeux : + ```rs fn play_game<G: Game>(mut game: G) { while !game.is_over() { @@ -565,6 +613,8 @@ fn play_game<G: Game>(mut game: G) { } ``` +On peut choisir le mode en entrant 1 ou 2, puis pour Tic Tac Toe entrer les coordonnées sous la forme `row col` et pour Nim en entrant le nombre de cailloux et à chaque tour le nombre que l'on prend : + ```rs fn main() { println!("\n--- Game engine demo ---");