Skip to main content

Structures, implémentations et traits

No Boilerplate → 🛰️ Building a space station in Rust (Simple Rust patterns 📚) [RUST-8]

Structure

Un struct ou structure est un type de donnée personnalisé qui permet de rassembler plusieurs propriétés. (C'est un peu comme un "objet" dans la POO)

// Création d'une première structure "Chapitre"
struct Chapitre {
  titre: String,
  numero: u64,
  contenu: String
}

// Création d'une deuxième structure "Livre" qui contient entreautre, une liste de "Chapitre"
struct Livre {
  titre: String,
  auteur: String,
  contenu: Vec<Chapitre>
}

fn main() {
    // Création d'un chapitre et d'un livre
    let chapitre = Chapitre {
        titre: "Mon super petit chapitre".to_string(),
        numero: 1,
        contenu: "voilà, le chapitre est fini...".to_string()
    };

    let livre = Livre {
        titre: "Livre court".to_string(),
        auteur: "Anonyme".to_string(),
        contenu: vec![chapitre]
    };

    println!("Mon livre est '{}' et le premier chapitre est '{}'", livre.titre, livre.contenu[0].titre);
}

Implémentation et trait

Un trait sert à définir un comportement partagé de manière abstraite par plusieurs types.

Une implémentation permet de grouper des méthodes et fonctions pour un type donné. Par exemple sur un struct, il va y avoir à la fois les propriétés du struct et les méthodes de son implémenation. La différence entre une fonction et une méthode c'est que:

  • Une méthode prends comme argument elle même (self) et on l'utilise en finissant par .nom_de_la_methode()
  • Une fonction n'a pas besoin d'accéder au contenu (donc pas de self), et on l'utilise en finissant par ::nom_de_la_fonction()

Ainsi une implémentation peut être utilisée pour implémenter un trait sur un type.

// Le trait "Resumable" dit que tout type implémentant ce trait doit avoir la méthode "resumer"
pub trait Resumable {
    // Le &self est important car sinon il devient propriétaire de lui même et on ne peut donc plus l'utiliser
    fn resumer(&self) -> String;
    // On utilise &mut pour pouvoir faire en sorte que l'objet puisse modifier une instance de lui même
    fn anonymiser(&mut self);

    // L'absence de self signifie que ceci est une fonction et non pas une méthode
    fn new(titre: &str, lieu: &str, auteur: &str, contenu: &str) -> ArticleDePresse;
}

// La structure ArticleDePresse contient 4 propriétés de type String
pub struct ArticleDePresse {
    pub titre: String,
    pub lieu: String,
    pub auteur: String,
    pub contenu: String,
}

// Implémente le trait Resumable pour le type ArticleDePresse
impl Resumable for ArticleDePresse {
    // Implémente la méthode "résumer"
    // Le &self est important car si on met juste 'self' il devient propriétaire de lui même et on ne peut donc plus l'utiliser
    fn resumer(&self) -> String {
        format!("{}, par {} ({})", self.titre, self.auteur, self.lieu)
    }

    // On utilise &mut pour pouvoir faire en sorte que l'objet puisse modifier une instance de lui même
    fn anonymiser(&mut self) {
        self.auteur = "Anonyme".to_string();
        self.lieu = "Inconnu".to_string();
    }

    // Ceci ne prends pas self, donc c'est une fonction et pas une méthode
    // Cette fonction va créer un élément de type ArticleDePresse à partir de valeurs données
    fn new(titre: &str, lieu: &str, auteur: &str, contenu: &str) -> ArticleDePresse {
        ArticleDePresse {
            titre: titre.to_string(),
            lieu: lieu.to_string(),
            auteur: auteur.to_string(),
            contenu: contenu.to_string()
        }
    }
}

// Crée ArticleDePresse dans la variable "article" et exécute la méthode "resumer"
fn main() {
    // On va utiliser la méthode anonymiser plus tard donc on met "mut" sur notre variable pour pouvoir la modifier
    // On utilise notre fonction new avec ::
    let mut article = ArticleDePresse::new("Mon titre", "Liège", "John Doe", "ayayyayayayaya");

    // On utilise nos méthodes avec .
    // On modifie notre article pour modifier les valeurs lieu et auteur
    article.anonymiser();

    // Enfin on retourne le résumé de notre article
    println!("{}", article.resumer());
}

Mais il est aussi possible de faire la même chose, sans trait.

// La structure ArticleDePresse contient 4 propriétés de type String
pub struct ArticleDePresse {
    pub titre: String,
    pub lieu: String,
    pub auteur: String,
    pub contenu: String,
}

// Implémente le type ArticleDePresse
impl ArticleDePresse {
    fn resumer(&self) -> String {
        format!("{}, par {} ({})", self.titre, self.auteur, self.lieu)
    }

    fn anonymiser(&mut self) {
        self.auteur = "Anonyme".to_string();
        self.lieu = "Inconnu".to_string();
    }

    fn new(titre: &str, lieu: &str, auteur: &str, contenu: &str) -> ArticleDePresse {
        ArticleDePresse {
            titre: titre.to_string(),
            lieu: lieu.to_string(),
            auteur: auteur.to_string(),
            contenu: contenu.to_string()
        }
    }
}

// Crée ArticleDePresse dans la variable "article" et exécute la méthode "resumer"
fn main() {
    // On va utiliser la méthode anonymiser plus tard donc on met "mut" sur notre variable pour pouvoir la modifier
    let mut article = ArticleDePresse::new("Mon titre", "Liège", "John Doe", "ayayyayayayaya");

    article.anonymiser();
    println!("{}", article.resumer());
}

En savoir plus