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(¬e);
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
Rcsau unRefCellpartajat în alt fir. Înlocuiește-le cuArcșiMutex. - Scrii
unsafe impl Sendca 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ă.