Rust : Common collections
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>