Cours de programmation séquentielle

Types avancés

Orestis Malaspinas

Vecteurs statiques

array

  • Collection d’objet de même type dont le nombre est connu à la compilation.
  • Ne peuvent pas changer de taille au cours de l’exécution (les éléments peuvent changer de valeur).
  • Éléments alloués contigument dans la mémoire.
  • Alloués sur la pile (en général).

fn main() {
    let entiers = [1, 2, 3, 4, 5];
    println!("Les 5 premiers entiers naturels: {:?}.", entiers); 

    let zeros: [i32; 10] = [0; 10]; // déclaration explicite du type
    println!("Dix zéros: {:?}.", zeros); 
}

Accès aux éléments

  • On accède aux éléments à l’aide de l’opérateur [index], où index est l’indice que nous voulons accéder.
  • L’indice va de 0 à taille-1.

    
    fn main() {
      let jours = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi",
                     "Samedi", "Dimanche"];
      let premier = jours[0];
      let dernier = jours[6];
    
      println!("Le premier jour de la semaine est le {} et le dernier {}.", premier, dernier); 
    }
      
  • Il est impossible d’accéder un indice au delà de la taille du vecteur.

    
    fn main() {
      let jours = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi",
                     "Samedi", "Dimanche"];
      let _oups = jours[7];
    }
      

Modification des éléments

  • Pour modifier les éléments utiliser mut.

fn main() {
    let mut entiers = [1, 2, 3, 4, 5];
    entiers[0] = -4;
    println!("Les 5 entiers : {:?}.", entiers); 
}
    

N-uplets

Tuples

  • Un ntuplet est une collection ordonnée de n objets.

    let tuple: (type1, type2, ...) = (val1, val2, ...); // le type peut être inféré
  • Permet de regrouper plusieurs valeurs avec différents types dans un type composé.

    
      fn main() {
          let tuple: (f64, char, usize) = (1.0, 'c', 18usize); // type pas nécessaire
    
          println!("Un tuple {:?}", tuple); // affichage Debug
    
          let sans_type_tuple = (666, "abcdef"); // type inféré
    
          println!("Un autre tuple {:?}", sans_type_tuple); // affichage Debug
    }
      
  • Pas forcément toujours pratique: on a pas d’identifiant pour les champs.

Exemple


fn area(w: usize, h: usize) -> usize {
    w * h
}

fn area_t(r: (usize, usize)) -> usize {
    r.0 * r.1
}

fn main() {
    let w = 10; 
    let h = 20;

    println!("Area = {}.", area(w,h));
    println!("Area with tuple = {}.", area_t((w,h)));
}

Destructuration

  • Les n objets d’un n-uple n’ont pas d’identifiant, juste un numéro.
  • On peut destructurer un n-uplet avec la notation

    let (a,b,c,...) = tuple;

  • Les champs sont accessibles avec la notation

    let a = tuple.0; let b = tuple.1;


fn main() {
    let tuple = (1.0, 'c', 18usize);
    let (fl, ch, us) = tuple;
    println!("Le tuple destructuré: {}, {}, {}", fl, ch, us);

    let fl_ind = tuple.0;
    let ch_ind = tuple.1;
    let us_ind = tuple.2;
    println!("Le tuple re-destructuré: {}, {}, {}", fl_ind, ch_ind, us_ind); 
}

Structures

struct (1/3)

  • Une structure est un type composé.
  • Permet de regrouper des et de nommer des données.
  • La syntaxe pour définir une structure est

    
    struct Identifiant {
      id1: Type,
      id2: Type,
      ...
    }
      
  • Exemple: définition d’un étudiant (diant, diant)

    
    struct EtudiantDiantDiant {
      nom: String,
      id: usize,
      actif: bool,
    }
    

struct (2/3)

  • Pour utiliser une structure il faut en créer une instance.
  • Pour ce faire il faut utiliser la syntaxe suivante

    
    let etu = EtudiantDiantDiant {
      nom: String::from("Jean-Paul Dax"), 
      id: 0usize,
      actif: true,
    };
    
  • Pour accéder aux champs d’une struct il faut utiliser la syntaxe instance.identifiant.
  • Par défaut tous les champs d’une structure sont immutables.
  • Pour les modifier il faut rendre toute la structure mutable.


let mut etu = EtudiantDiantDiant {
    nom: String::from("Jean-Paul Dax"), 
    id: 0usize,
    actif: true,
};

etu.actif = false;

struct (3/3)

  • En ne donnant pas d’identifiant aux champs d’un struct, on crée un tuple struct.
  • On accède aux champs comme avec un tuple normal .0, .1, …
  • Pour ce faire il faut utiliser la syntaxe suivante

fn main() {
  struct Point2d(i32, i32);

  let origine = Point2d(0,0);
  println!("L'origine est le point ({}, {}).", origine.0, origine.1);
}

Types énumérés

Enum (1/2)

  • Permet de définir un type en énumérant toutes ses valeurs possibles.

    
    enum TypeEnum {
      id1,
      id2,
      ...
    }
      
  • On peut créer une instance de chacune des variantes du type énuméré

    
    let ins1 = TypeEnum::id1;
    let ins2 = TypeEnum::id2;
    ...
      
  • Important: TypeEnum::id1 et TypeEnum::id2 sont tous les deux de type TypeEnum.

    
      enum TypeEnum {
          id1,
          id2,
      }
    
      fn foo(enum_type: TypeEnum) { }
    
      fn main() {
          foo(TypeEnum::id1); // appel valide
          foo(TypeEnum::id2); // appel également valide
      }
      

Enum (2/2)

  • On peut également associer des valeurs avec n’importe quel variante de l’enum.

struct TypeStruct {
    // détails pas importants
};

enum TypeEnum {
    id1(char, char), // deux char
    id2{x: usize},   // struct anonyme 
    id3(TypeStruct), // struct
}

let ins1 = TypeEnum::id1('a','b');
let ins2 = TypeEnum::id2({x: 12usize});
let ins2 = TypeEnum::id3(TypeStruct::new());

Pattern matching

match (1/3)

  • Structure de contrôle extrêmement puissante.
  • Comparaison entre une valeurs et une série de patterns afin de décider d’une action à effectuer.
  • Toutes les possibilités sont obligatoirement traitées.

enum TypeEnum {
    id1,
    id2,
    id3,
}

fn main() {
    let data = TypeEnum::id1;
    let num = 
        match data {
            TypeEnum::id1 => 1,
            TypeEnum::id2 => 2,
            TypeEnum::id3 => 3,
        };
    println!("num = {}", num);
}

match (2/3)

  • Dans certains cas, on veut le même comportement pour plusieurs patterns.

enum TypeEnum {
    id1,
    id2,
    id3,
}

fn main() {
    let data = TypeEnum::id3;
    let num = 
        match data {
            TypeEnum::id1 => 1,
            _ => 2,
        };
    println!("num = {}", num);
}

match (3/3)

  • Pour lier une valeur stockée dans un Enum

enum AnimalMythique {
    Chupacabra,
    Dahu,
    ChevalAile(Pays), // Le type Pays est défini avant
}

fn pays_dorigine(animal: AnimalMythique) {
    match animal {
        AnimalMythique::Chpacabra => println!("Mexique"),
        AnimalMythique::Dahu => println!("Suisse"), // Oui il Suisse.
        AnimalMythique::ChevalAile(pays) => println!("{}", pays);
    },
}