---
title: Cours de programmation séquentielle
subtitle: Traits
author: Orestis Malaspinas
sansfont: Sans Serif
---

# Traits

## Généralités

- Un `Trait`{.rust} permet de dire au compilateur quelles fonctionalités un type doit posséder.
- Permet de conceptualiser les foncionalités partagées entre types.
- On peut contraindre un type générique à avoir un certain trait (*trait bound*).

    <pre><code data-trim="hljs rust">
    fn max&lt;T: PartialOrd&gt;(a: T, b: T) -> T {
        if a > b { a } else { b } // si T ne peut pas être ordonné, "&lt;" et "&gt;" n'existent pas
    }                             
    </code></pre>

- Similaire aux *interfaces* de certains langages.

## Définition d'un trait

- Dans un trait sont regroupées des méthodes qu'un type peut appeler.
- Dans leur définition on ne connaît que leurs signatures.
- Exemple:

    <pre><code data-trim="hljs rust">
    trait Animal { // définition d'un trait
        fn cri(&self) -> String;

        fn nom(&self) -> &String;
    } // le nombre de méthode est arbitraire
    </code></pre>

## Implémentation d'un trait pour un type (1/2)

- Tout type qui implémentera un trait devra implémenter **toutes** ses méthodes.
- Le compilateur saura que tout type implémentant un trait peut appeler ses méthodes.

    <pre><code data-trim="hljs rust" class="lang-rust">
    trait Animal { // définition d'un trait
        fn cri(&self) -> String;

        fn nom(&self) -> &String;
    } // le nombre de méthode est arbitraire

    struct Chien {
        nom: String,
    }

    impl Animal for Chien { // le bloc où les méthodes du trait sont implémentées
        fn cri(&self) -> String {
            String::from("Ouaf!")
        }

        fn nom(&self) -> &String {
            &self.nom
        }
    }

    fn main() {
        let chien = Chien{ nom: String::from("Jack")};
        println!("Le cri de {} est {}", chien.nom(), chien.cri());
    }
    </code></pre>

## Implémentation d'un trait pour un type (2/2)

<pre><code data-trim="hljs rust" class="lang-rust">
trait Animal { // définition d'un trait
    fn cri(&self) -> String;

    fn nom(&self) -> &String;
} // le nombre de méthode est arbitraire

struct Chien {
    nom: String,
}

impl Animal for Chien { // le bloc où les méthodes du trait sont implémentées
    fn cri(&self) -> String {
        String::from("Ouaf")
    }

    fn nom(&self) -> &String {
        &self.nom
    }
}

struct Chat {
    nom: String,
}

impl Animal for Chat { // le bloc où les méthodes du trait sont implémentées
    fn cri(&self) -> String {
        String::from("Miaou")
    }

    fn nom(&self) -> &String {
        &self.nom
    }
}

fn main() {
    let chien = Chien{ nom: String::from("Jack")};
    println!("Le cri de {} est {}.", chien.nom(), chien.cri());

    let chat = Chat{ nom: String::from("Rémy")};
    println!("Le cri de {} est {}.", chat.nom(), chat.cri());
}
</code></pre>

## Implémentations par défaut

- Il est pratique d'avoir des implémentations par défaut (les mêmes pour tous les types implémentant un trait).

    <pre><code data-trim="hljs rust" class="lang-rust">
    trait Animal { // définition d'un trait
        fn cri(&self) -> String;

        fn cri_puissant(&self) -> String {
            self.cri().to_uppercase()
        }
    } // le nombre de méthode est arbitraire

    struct Chien { }

    impl Animal for Chien {
        fn cri(&self) -> String {
            String::from("Ouaf")
        }
    }

    struct Chat { }

    impl Animal for Chat { // le bloc où les méthodes du trait sont implémentées
        fn cri(&self) -> String {
            String::from("Miaou")
        }
    }

    fn main() {
        let chien = Chien{};
        println!("Le cri du chien est {} et son cri puissant est {}.",
            chien.cri(), chien.cri_puissant());

        let chat = Chat{};
        println!("Le cri du chat est {} et son cri puissant est {}.",
            chat.cri(), chat.cri_puissant());
    }
    </code></pre>

## Trait bound ("contrainte de trait")

- Lors de la définition d'un générique, on peut dire au compilateur si le type implémente un trait.

    <pre><code data-trim="hljs rust" class="lang-rust">
    // Cette fonction ne peut pas foncitonner pour tous les types
    // T implémente PartialEq (les opérateurs &lt;, &gt;)
    fn max&lt;T: PartialOrd&gt;(a: T, b: T) -> T {
        if a > b { a } else { b } // si on peut pas comparer a et b
    }                             // cela ne peut pas compiler, d'où
                                // le PartialOrd
    fn main() {
        let a = 1;
        let b = 7;

        println!("De {} et {}, {} est le plus grand.", a, b, max(a,b));

        let a = 1.5;
        let b = 7.5;

        println!("De {} et {}, {} est le plus grand.", a, b, max(a,b));
    }
    </code></pre>

## Règle pour l'implémentation d'un trait

- On peut implémenter un trait seulement si le trait ou le type est local à notre librairie (*crate*).
- Impossible de réimplémenter l'addition pour les `f64`{.rust} (le trait `Add`{.rust} et le type `f64`{.rust}) sont dans la librairie standard, donc 
pas dans une librairie locale.
