---
title: Cours de programmation séquentielle
subtitle: Types génériques
author: Orestis Malaspinas
sansfont: Sans Serif
---

# Types génériques

## Généralités

- Les types génériques sont utilisés dans des fonctions, structs, enums, ...
- Permettent d'utiliser différents types concrets.
- Permettent de réduire drastiquement la quantité de code à écrire et de généraliser les concepts.

## Exemple (1/2)

<pre><code data-trim="hljs rust" class="lang-rust">
fn max_i32(a: i32, b: i32) -> i32 {
    if a > b { a } else { b }
}

fn max_f64(a: f64, b: f64) -> f64 {
    if a > b { a } else { b }
}

fn main() {
    let a = 1;
    let b = 7;

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

    let a = 1.5;
    let b = 7.5;

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

## Exemple (2/2)

<pre><code data-trim="hljs rust" class="lang-rust">
// Cette fonction ne peut pas foncitnner pour tous les types
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>

# Dans les `struct`

## Un seul type générique

<pre><code data-trim="hljs rust" class="lang-rust">
// Generic point
#[derive(Debug)]
struct Point&lt;T&gt; {
    x: T,
    y: T,
}

fn main() {
    let int_point = Point{ x: 1, y: 2};     // i32 point
    let flt_point = Point{ x: 2.3, y: 4.7}; // f64 point
    // let does_not_work_point = Point{ x: 1, y: 1.5}
}
</code></pre>

## Deux types génériques

<pre><code data-trim="hljs rust" class="lang-rust">
// Generic point
#[derive(Debug)]
struct Point&lt;T, U&gt; {
    x: T,
    y: U,
}

fn main() {
    let int_point = Point{ x: 1, y: 2};     // i32 point
    let flt_point = Point{ x: 2.3, y: 4.7}; // f64 point
    let does_work_now_point = Point{ x: 1, y: 1.5}; // i32, f64 point
}
</code></pre>

# Dans les méthodes

## Exemples

<pre><code data-trim="hljs rust" class="lang-rust">
struct Point&lt;T&gt; {
    x: T,
    y: T,
}

impl&lt;T&gt; Point&lt;T&gt; {
    fn get_x(&self) -> &T {
        &self.x
    }
}

impl Point&lt;f64&gt; {
    fn dist(&self) -> f64 {
        (self.x*self.x + self.y*self.y).sqrt()
    }
}

fn main() {
    let point = Point{ x: 1, y: 2};
    println!("x = {}", point.get_x());
    let point = Point{ x: 1.0, y: 2.0};
    println!("distance = {} et x = {}", point.dist(), point.get_x());
}
</code></pre>

# Dans les enums

## L'`Option<T>`{.rust}

<pre><code data-trim="hljs rust" class="lang-rust">
// Permet d'avoir un type générique dans Some
enum Some&lt;T&gt; {
    Some(T),
    None,
}

fn main() {
    let int_opt = Some(4);
    let char_opt = Some('a');
}
</code></pre>

# Considérations philosophiques

Que fait le compilateur?

- A la compilation le type générique est remplacé par un type spécifique: monomorphisation.
- Pour chaque type, le compilateur crée une version de la fonction, struct, ...
- Et remplace la fonction générale par le code spécialisé.
- Aucun coût à l'exécution.