Exercices Rust : déplacement, durée de vie, pointeurs intelligents et concurrence

Transfert de propriété

fn main() {
    let v = Vec::new();
    let mut w = remplir(v);
    println!("w contient {} éléments : {:?}", w.len(), w);
    w.push(88);
    println!("w contient {} éléments : {:?}", w.len(), w);
    // println!("v contient {} éléments : {:?}", v.len(), v);  // interdit
}

fn remplir(mut v: Vec<i32>) -> Vec<i32> {
    v.extend([22, 44, 66]);
_id().unwrap()]);
    v
}

Variante avec emprunt mutable

fn main() {
    let mut v = Vec::new();
    remplir_ref(&mut v);
    println!("v contient {} éléments : {:?}", v.len(), v);
    v.push(88);
    println!("v contient {} éléments : {:?}", v.len(), v);
}

fn remplir_ref(v: &mut Vec<i32>) {
    v.extend([22, 44, 66]);
}

Variante avec clonage

fn main() {
    let mut v = Vec::new();
    let mut w = remplir_clone(v.clone());
    println!("w contient {} éléments : {:?}", w.len(), w);
    w.push(88);
    println!("w contient {} éléments : {:?}", w.len(), w);
}

fn remplir_clone(mut v: Vec<i32>) -> Vec<i32> {
    v.extend([22, 44, 66]);
    v
}

Vraiante sans paramètre

fn main() {
    let mut v = construire();
    println!("v contient {} éléments : {:?}", v.len(), v);
    v.push(88);
    println!("v contient {} éléments : {:?}", v.len(), v);
}

fn construire() -> Vec<i32> {
    vec![22, 44, 66]
}

Manipulation de références

fn main() {
    let mut x = 100;
    let r1 = &mut x;
    *r1 += 100;
    let r2 = &mut x;
    *r2 += 1000;
    assert_eq!(x, 1200);

    let texte = "Rust is great!".to_string();
    dernier_caractere(&texte);
    majuscules(texte);
}

fn dernier_caractere(s: &String) -> char {
    s.chars().last().unwrap()
}

fn majuscules(mut s: String) {
    s.make_ascii_uppercase();
    println!("{}", s);
}

Durées de vie

struct Livre<'a> {
    auteur: &'a str,
    titre: &'a str,
}

fn main() {
    let a = String::from("abcd");
    let b = "xyz";
    let plus_long = le_plus_long(&a, b);
    println!("La plus longue chaîne est '{}'", plus_long);

    let nom = String::from("Jill Smith");
    let titre = String::from("Fish Flying");
    let livre = Livre { auteur: &nom, titre: &titre };
    println!("{} par {}", livre.titre, livre.auteur);
}

fn le_plus_long<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

Pointeurs intelligents

Box

#[derive(Debug, PartialEq)]
enum Liste {
    Cons(i32, Box<Liste>),
    Vide,
}

fn main() {
    let vide = creer_vide();
    let pleine = creer_pleine();
    println!("Liste vide : {:?}", vide);
    println!("Liste pleine : {:?}", pleine);
    assert_eq!(vide, Liste::Vide);
    assert_ne!(vide, pleine);
}

fn creer_vide() -> Liste { Liste::Vide }
fn creer_pleine() -> Liste { Liste::Cons(1, Box::new(Liste::Vide)) }

Rc

use std::rc::Rc;

#[derive(Debug)]
struct Soleil {}

#[derive(Debug)]
enum Planete {
    Mercure(Rc<Soleil>), Venus(Rc<Soleil>), Terre(Rc<Soleil>),
    Mars(Rc<Soleil>), Jupiter(Rc<Soleil>), Saturne(Rc<Soleil>),
    Uranus(Rc<Soleil>), Neptune(Rc<Soleil>),
}

impl Planete {
    fn saluer(&self) { println!("Salut depuis {:?}!", self) }
}

fn main() {
    let soleil = Rc::new(Soleil {});
    println!("compteurs = {}", Rc::strong_count(&soleil));

    let mercure = Planete::Mercure(Rc::clone(&soleil));
    println!("compteurs = {}", Rc::strong_count(&soleil));
    mercure.saluer();

    macro_rules! planete {
        ($n:ident, $var:ident, $p:path) => {
            let $var = $p(Rc::clone(&soleil));
            println!("compteurs = {}", Rc::strong_count(&soleil));
            $var.saluer();
        };
    }

    planete!(venus, v, Planete::Venus);
    planete!(terre, t, Planete::Terre);
    planete!(mars, m, Planete::M);
    planete!(jupiter, j, Planete::Jupiter);
    planete!(saturne, s, Planete::Saturne);
    planete!(uranus, u, Planete::Uranus);
    planete!(neptune, n, Planete::Neptune);

    assert_eq!(Rc::strong_count(&soleil), 9);

    drop(n);
    drop(u);
    drop(s);
    drop(j);
    drop(m);
    drop(t);
    drop(v);
    drop(mercure);
    assert_eq!(Rc::strong_count(&soleil), 1);
}

Arc

use std::sync::Arc;
use std::thread;

fn main() {
    let valeurs: Vec<_> = (0..100u32).collect();
    let partage = Arc::new(valeurs);
    let mut taches = vec![];

    for decalage in 0..8 {
        let nombres = Arc::clone(&partage);
        taches.push(thread::spawn(move || {
            let somme: u32 = nombres.iter()
                .filter(|&n| n % 8 == decalage)
                .sum();
            println!("Somme décalage {} = {}", decalage, somme);
        }));
    }
    for t in taches { t.join().unwrap(); }
}

Cow

use std::borrow::Cow;

fn abs_tout<'a>(entree: &'a mut Cow<'_, [i32]>) -> &'a mut Cow<'_, [i32]> {
    for i in 0..entree.len() {
        if entree[i] < 0 {
            entree.to_mut()[i] = -entree[i];
        }
    }
    entree
}

fn main() {
    let s = [0, 1, 2];
    let mut c = Cow::from(&s[..]);
    match abs_tout(&mut c) {
        Cow::Borrowed(v) => println!("Emprunt : {:?}", v),
        _ => panic!("devrait être emprunté"),
    }

    let s = [-1, 0, 1];
    let mut c = Cow::from(&s[..]);
    match abs_tout(&mut c) {
        Cow::Owned(v) => println!("Copié : {:?}", v),
        _ => panic!("devrait être copié"),
    }

    let v = vec![-1, 0, 1];
    let mut c = Cow::from(v);
    match abs_tout(&mut c c) {
        Cow::Owned(v) => println!("Déjà propriétaire : {:?}", v),
        _ => panic!("devrait être propriétaire"),
    }
}

Concurrence

Lancement simple

use std::thread;
use std::time::Duration;

fn main() {
    let mut handles = vec![];
    for i in 0..10 {
        handles.push(thread::spawn(move || {
            thread::sleep(Duration::from_millis(250));
            println!("thread {} terminé", i);
        }));
    }
    let mut finis = 0;
    for h in handles {
        h.join().unwrap();
        finis += 1;
    }
    assert_eq!(finis, 10);
}

Partage d'état avec Mutex

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

struct Compteur {
    termines: u32,
}

fn main() {
    let cpt = Arc::new(Mutex::new(Compteur { termines: 0 }));
    let mut taches = vec![];
    for _ in 0..10 {
        let c = Arc::clone(&cpt);
        taches.push(thread::spawn(move || {
            thread::sleep(Duration::from_millis(250));
            c.lock().unwrap().termines += 1;
        }));
    }
    for t in taches {
        t.join().unwrap();
        println!("tâches terminées : {}", cpt.lock().unwrap().termines);
    }
}

Canal MPSC

use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::time::Duration;

struct File {
    taille: u32,
    debut: Vec<u32>,
    fin: Vec<u32>,
}

impl File {
    fn nouvelle() -> Self {
        File {
            taille: 10,
            debut: vec![1, 2, 3, 4, 5],
            fin: vec![6, 7, 8, 9, 10],
        }
    }
}

fn envoyer(f: File, tx: mpsc::Sender<u32>) {
    let f = Arc::new(f);
    let f1 = Arc::clone(&f);
    let tx1 = tx(tx.clone());

    thread::spawn(move || {
        for &v in &f1.debut {
            tx1.send(v).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    thread::spawn(move || {
        for &v in &f.fin {
            tx.send(v).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });
}

fn main() {
    let (tx, rx) = mpsc::channel();
    let file = File::nouvelle();
    let longueur = file.taille;

    envoyer(file, tx);

    let mut recus = 0;
    for v in rx {
        println!("Reçu : {}", v);
        recus += 1;
    }
    println!("Total reçu : {}", recus);
    assert_eq!(recus, longueur);
}

Étiquettes: Rust move-semantics lifetimes smart-pointers rc

Publié le 2 août à 01h43