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);
}