Rust : Common collections

De Justine's wiki
Aller à la navigation Aller à la recherche

<syntaxhighlight lang='rust'> fn main() {

   // 1 - Vectors
   // Equivalent d'une liste en Python;
   // Un vector est une collection de valeurs du *même type*
   //Déclaration d'un vector de i32 vide
   let v: Vec<i32> = Vec::new();
   //Déclaration avec des valeurs
   let v2 = vec![1, 2, 3, 4, 5];
   //Update d'un vector mutable
   let mut v3 = Vec::new();
   v3.push(5);
   v3.push(6);
   //Lecture
   //Via index, ici, pas de gestion d'erreur, cette méthode panique si
   //on référence un élement non existant
   let third: &i32 = &v2[2];
   println!("3rd element is {}", third);
   //Via get
   //On utilise une option au cas où on ne récupère rien
   //On a ainsi de la gestion d'erreur car cette méthode peut renvoyer None
   let third: Option<&i32> = v2.get(2);
   match third {
       Some(third) => println!("The third element is {}", third),
       None => println!("There is no third element."),
   }
   //A se rappeller, on ne peut pas avoir un emprunt mutable
   //et un emprunt non mutable dans le même scope
   let mut v4 = vec![1, 2, 3];
   let first = &v4[0];
   //Ne fonctionnera pas
   //v.push(6);
   
   //Itération
   //non mutable
   let v5 = vec![1, 2, 3, 69];
   for i in &v {
       println!("{}", i);
   }
   //mutable
   //Ici on utilise * pour déréférencer i avant de le modifier.
   //Ce n'est pas comme Python, donc !
   let mut v6 = vec![100, 32, 57];
   for i in &mut v6 {
       *i += 50;
   }


   //Utiliser un enum pour stocker plusieurs types de variables
   //On peut contourner le problème des types identiques au sein d'un Vector
   //via un enum.
   enum SpreadsheetCell {
       Int(i32),
       Float(f64),
       Text(String),
   }
   let row = vec![
       SpreadsheetCell::Int(3),
       SpreadsheetCell::Text(String::from("blue")),
       SpreadsheetCell::Float(10.12),
   ];
   //Le vector a des méthodes : https://doc.rust-lang.org/std/vec/struct.Vec.html
   //Comme les autres structs, le vector est droppé quand on sort de son scope.
   //
   //------------------------------------------------------------------------------
   //2 - Strings
   //Un string est une collection de chars !
   //Il est growable, mutable, owned, UTF-8 encoded 
   //Il ne faut pas confondre String et str, aussi appellé "string litteral" ou slice (en fonction de la situation)
   //qui est juste une
   //collection de chars quelque part en mémoire, qui ne peut fonctionner qu'après emprunt (&str)
   //C'est un peu naze !
   //Création
   //Vide
   let mut s = String::new();
   //Depuis un litteral
   //Plusieurs façons
   let data = "Initial data";
   let s = data.to_string();
   let s = "Initial data".to_string();
   let s = String::from("initial contents");
   //UTF-8 permet plein de choses
   let hello = String::from("السلام عليكم");


   //Update
   //push_str prend une slice en argument; on ne veut pas forcément s'approprier le contenu
   let mut s = String::from("foo");
   s.push_str("_bar");
   //Exemple : ici, s2 ne fonctionnera pas si mon push sur s1 ne se fait pas via un emprunt
   let mut s1 = String::from("foo");
   let s2 = "bar";
   s1.push_str(s2);
   //Ne fonctionnera pas, push_str a pris la possession de s1
   //println!("s2 is {}", s2);
   
   //push() prend seulement un char
   let mut s = String::from("Hello");
   s.push('!');


   //Concaténation
   let s1 = String::from("Hello, ");
   let s2 = String::from("world!");
   let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
   //La fonction add (le + ici) a pour signature
   //fn add(self, s: &str) -> String
   //On voit que l'argument est un &str, ce que je n'ai pas utilisé au-dessus;
   //mais le compilateur se démerde (pour une fois) et transforme mon &s2 en &s2[..]
   //En réalité add prend possession de s1 et lui colle le contenu de s2 à la fin
   //De toute façon, on peut préférer utiliser !format qui est plus clair
   //Et utilise implicitement des références
   let s1 = String::from("tic");
   let s2 = String::from("tac");
   let s3 = String::from("toe");
   let s = format!("{}-{}-{}", s1, s2, s3);
   println!("{s2}");


   //Index
   //On ne peut pas utiliser d'index sur un string:
   let s1 = String::from("hello");
   //Nope...
   //let h = s1[0];
   
   //En réalité, un string est un wrapper fonctionnant sur un Vector<u8>.
   //Les code points unicode ne font pas tous la même taille : une lettre latine prend 1 octet,
   //une lettre cyrillique en prend 2 par exemple. Du coup, dans ma collection de u8, je ne sais pas lesquels
   //correspondent à une lettre... Donc, pas d'index.
   //En plus, on peut représenter un string unicode en tant que vector de u8, ensemble de valeurs
   //scalaire unicodes (qui peuvent être des diacritiques par exemples, et donc pas vraiment des
   //lettres) et en tant que clusters de graphemes, soit les lettres avec les diacritiques
   //appliqués.
   //
   //Il est donc important de savoir si on opère sur les octets du string ou ses caractères !
   //
   //Hé ben merde alors !
   
   //Slicing
   //Plutôt que d'indexer, on peut faire des slices...
   let hello = "Здравствуйте";
   let s = &hello[0..4];
   //s contiendra les 4 premier *octets* (et pas forcément lettres, donc)
   //Si je coupe au milieu d'un charactère, ça plante !
   //s = &hello[0..1]
   //Iterer
   //On peut itérer sur les caractères (ouf)
   //Même si ça ne marche pas forcément sur des charactères complexes comme
   //avec des diacritiques
   for c in "Зд".chars() {
       println!("{}", c);
   }
   //Ou sur les octets
   for b in "Зд".bytes() {
       println!("{}", b);
   }
   //Nous affichera des chiffres
   //
   //Pour citer la doc : Strings Are Not So Simple. On doit appréhender la complexité, mais
   //en échange on a pas d'erreurs sur des caractères chiants
   //---------------------------------------------------------------------------------------
   //
   //3 - hash maps
   //Equivalent d'un dictionnaire en Python : HashMap<K, V>
   //Se base sur un algorithme de hashage pour relier clef et valeur
   //Rust utilise SipHash (https://en.wikipedia.org/wiki/SipHash) qui résiste aux attaque DOS
   //utilisant des tables de hashage
   //Ce n'est pas le plus rapide mais il est safe; cela dit on peut changer 
   //Checker la lib standard pour les méthodes ! Attention, la hashmap n'est pas incluse par
   //défaut.
   
   //Création
   //Avec insertion
   use std::collections::HashMap;
   let mut scores = HashMap::new();
   scores.insert(String::from("Blue"), 10);
   scores.insert(String::from("Yellow"), 50);
   //Accès aux valeurs : utiliser get
    let mut scores = HashMap::new();
   scores.insert(String::from("Blue"), 10);
   scores.insert(String::from("Yellow"), 50);
   let team_name = String::from("Blue");
   let score = scores.get(&team_name).copied().unwrap_or(0);
   //Get renvoie une Option<&V>, on peut se récupérer un None.
   //Ici on utilise copied() pour récupérer un <i32> au lieu d'un <&i32>
   //et unwrap_or(0) pour avoir soit la valeur, soit 0
   
   //Itération
   for (key, value) in &scores {
       println!("{}: {}", key, value);
   }
   //Ownership
   //Quand une hashmap récupère une valeur, 2 possibilités:
   //* Soit la valeur a le trait copy (comme i32) et alors elle est copiée
   //* Soit elle ne l'as pas (comme String) et alors elle est réapropriée
   let field_name = String::from("Favorite color");
   let field_value = String::from("Blue");
   let mut map = HashMap::new();
   map.insert(field_name, field_value);
   // field_name and field_value are invalid at this point, try using them and
   // see what compiler error you get! (Value borrowed after move)
   //On peut insérer des références, mais il faut alors s'assurer que les 
   //items référencés sont valides (lifetime)
   //Update
   //Si je réinsère une valeur sur une clef existante, elle est remplacée, bien sûr.
   let mut scores = HashMap::new();
   scores.insert(String::from("Blue"), 10);
   scores.insert(String::from("Blue"), 25);
   println!("{:?}", scores);
   //Je peux choisir d'ajouter une clef - valeur seulement si la clef n'existe pas
    let mut scores = HashMap::new();
   scores.insert(String::from("Blue"), 10);
   scores.entry(String::from("Yellow")).or_insert(50);
   scores.entry(String::from("Blue")).or_insert(50);
   println!("{:?}", scores);
   //...ou en se basant sur une valeur déjà existante
       let text = "hello world wonderful world";
   let mut map = HashMap::new();
   for word in text.split_whitespace() {
       let count = map.entry(word).or_insert(0);
       *count += 1;
   }
   println!("{:?}", map);

} </syntaxhighlight>