Rust : Common collections

De Justine's wiki
Version datée du 3 novembre 2022 à 09:15 par Justine (discussion | contributions) (Page créée avec «  <nowiki> 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 p... »)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigation Aller à la recherche
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);

}