Rust

Any donation is very welcome
Fork me on GitHub

II. Spécificités de Rust

12. Box

Le type Box est "tout simplement" un pointeur sur des données stockées "sur le tas" (la "heap" donc).

On s'en sert notamment quand on veut éviter de trop surcharger la pile (la "stack") en instanciant directement "sur le tas".

Ou pour avoir une adresse constante quand on utilise une FFI (Foreign Function Interface), comme des pointeurs sur objet/fonction. Je n'en parlerai pas dans cette partie du cours.

Il existe le mot-clé box pour en créer une, par-contre, la feature le permettant est encore instable donc le code suivant ne fonctionnera pas si vous utilisez la version stable du compilateur :

Run#![feature(box_syntax)]

let a: Box<[u8; 100000]> = Box::new([0; 100000]); // Bof...
let b: Box<[u8; 100000]> = box new([0; 100000]; // Mieux ! Mais ne marche qu'en nightly...

Pour mieux illustrer ce qu'est le type Box, je vous propose deux exemples :

Structure récursive

On s'en sert aussi dans le cas où on ignore quelle taille fera le type, comme les types récursifs par exemple :

Run#[derive(Debug)]
enum List<T> {
    Element(T, List<T>),
    Vide,
}

fn main() {
    let list: List<i32> = List::Element(1, List::Element(2, List::Vide));
    println!("{:?}", list);
}

Si vous essayez de compiler ce code, vous obtiendrez une magnifique erreur : "invalid recursive enum type". (Notez que le problème sera le même si on utilise une structure). Ce type n'a pas de taille définie, nous obligeant à utiliser un autre type qui lui en a une (donc & ou bien Box) :

Run#[derive(Debug)]
enum List<T> {
    Element(T, Box<List<T>>),
    Vide,
}

fn main() {
    let list: List<i32> = List::Element(1, Box::new(List::Element(2, Box::new(List::Vide))));
    println!("{:?}", list);
}

Liste chaînée

Box est également utile pour la création de listes chaînées :

Runuse std::fmt::Display;

struct List<T> {
    a: T,
    next: Option<Box<List<T>>>, // None signifiera qu'on est à la fin de la chaîne
}

impl<T> List<T> {
    pub fn new(a: T) -> List<T> {
        List {
            a: a,
            next: None,
        }
    }

    pub fn add_next(&mut self, a: T) {
        match self.next {
            Some(ref mut n) => n.add_next(a),
            None => {
                self.next = Some(Box::new(List::new(a)));
            }
        }
    }
}

impl<T: Display> List<T> {
    pub fn display_all_list(&self) {
        println!("-> {}", self.a);
        match self.next {
            Some(ref n) => n.display_all_list(),
            None => {}
        }
    }
}

fn main() {
    let mut a = List::new(0u32);

    a.add_next(1u32);
    a.add_next(2u32);
    a.display_all_list();
}

Voilà pour ce petit chapitre rapide. Box est un type important auquel les gens ne pensent pas forcément alors qu'il pourrait résoudre leur problème. Il me semblait donc important de vous en parler.