Send și Sync în Rust

Trait-uri marker automate prin care compilatorul previne erorile de concurență: Send = poate trece în alt fir, Sync = poate fi împărțit.

Sintaxă
pub unsafe auto trait Send { }   // poate fi MUTAT în alt fir
pub unsafe auto trait Sync { }   // poate fi FOLOSIT prin &T din mai multe fire

Ce face Send și Sync

Send înseamnă că o valoare poate fi mutată într-un alt fir de execuție. Sync înseamnă că o referință &T poate fi folosită din mai multe fire în același timp (altfel spus, &T este Send). Nu au metode și nu le implementezi de obicei: compilatorul le deduce singur, câmp cu câmp. Un tip este Send dacă toate câmpurile lui sunt Send.

Aproape totul este Send și Sync. Excepțiile importante: Rc (numărătoarea nu este atomică; în fire folosești Arc), Cell și RefCell (sunt Send, dar nu Sync; în fire folosești Mutex sau RwLock) și pointerii bruți. thread::spawn cere ca tot ce mută în fir să fie Send, așa că greșelile de concurență devin erori de compilare, nu bug-uri la rulare.

Implementarea manuală (unsafe impl Send for Tip {}) este necesară doar pentru tipuri cu pointeri bruți și înseamnă că îți asumi corectitudinea.

Ce returnează

Nu au metode; sunt condiții verificate la compilare.

Exemple

Exemplul 1: Arc> este Send și Sync

Fiecare fir adaugă câte o notă. Firele sunt așteptate cu join, iar notele sunt sortate, deci rezultatul este același la fiecare rulare.

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

fn main() {
    let note = Arc::new(Mutex::new(Vec::new()));
    let mut fire = Vec::new();
    for (nume, nota) in [("Ana", 10), ("Radu", 8), ("Ioana", 9)] {
        let note = Arc::clone(&note);
        fire.push(thread::spawn(move || {
            note.lock().unwrap().push((nota, nume));
        }));
    }
    for f in fire {
        f.join().unwrap();
    }
    let mut toate = note.lock().unwrap().clone();
    toate.sort();
    println!("{toate:?}");
}

Rezultat

[(8, "Radu"), (9, "Ioana"), (10, "Ana")]

Exemplul 2: Verificare la compilare: cine este Send și Sync

use std::cell::RefCell;
use std::sync::{Arc, Mutex};

fn este_send<T: Send>() {}
fn este_sync<T: Sync>() {}

fn main() {
    este_send::<String>();
    este_sync::<String>();
    este_send::<Arc<Mutex<Vec<u32>>>>();
    este_sync::<Arc<Mutex<Vec<u32>>>>();
    este_send::<RefCell<u32>>();          // RefCell: Send, dar nu Sync
    // este_sync::<RefCell<u32>>();       // nu compilează
    // este_send::<std::rc::Rc<u32>>();   // nu compilează
    println!("toate verificările au trecut la compilare");
}

Rezultat

toate verificările au trecut la compilare

Exemplul 3: Rc nu poate trece în alt fir

use std::rc::Rc;
use std::thread;

fn main() {
    let date = Rc::new(vec![1, 2, 3]);
    let copie = Rc::clone(&date);
    let fir = thread::spawn(move || {
        println!("{:?}", copie);
    });
    fir.join().unwrap();
}

Eroarea de compilare (rustc, prescurtată)

error[E0277]: `Rc<Vec<i32>>` cannot be sent between threads safely
 --> main.rs:7:29
  |
7 |       let fir = thread::spawn(move || {
  |                 ------------- ^------
  |                 |             |
  |  _______________|_____________within this `{closure@main.rs:7:29: 7:36}`
  | |               |
  | |               required by a bound introduced by this call
8 | |         println!("{:?}", copie);
9 | |     });
  | |_____^ `Rc<Vec<i32>>` cannot be sent between threads safely
  |
  = help: within `{closure@main.rs:7:29: 7:36}`, the trait `Send` is not implemented for `Rc<Vec<i32>>`

Greșeli frecvente

  • Încerci să treci un Rc sau un RefCell partajat în alt fir. Înlocuiește-le cu Arc și Mutex.
  • Scrii unsafe impl Send ca să scapi de eroare. Eroarea semnalează o problemă reală; aproape niciodată nu este soluția.

Comenzi înrudite

  • Sized: Trait marker pentru tipurile cu dimensiune cunoscută la compilare; ?Sized permite și str, [T], dyn Trait.
  • Any: Trait-ul care permite aflarea tipului concret la rulare: downcast_ref::<T>() pe &dyn Any sau Box<dyn Any>.
  • Clone: Trait-ul pentru copierea explicită a unei valori cu .clone(); se obține cu #[derive(Clone)] sau implementare manuală.