In Rust, Send and Sync are two key traits that define how data can be safely shared or moved between threads.
Send: Moving Ownership Across Threads
A type is Send if it can be safely transferred from one thread to another and Rust automatically derives Send for most types, as long as all their components are Send.
use std::thread;
fn main() {
let data = String::from("Hello, world!");
let handle = thread::spawn(move || {
println!("{}", data);
});
handle.join().unwrap();
}Here, String is Send, so it can be moved safely to the new thread.
Sync: Sharing References Across Threads
A type is Sync if it is safe to access references to it from multiple threads simultaneously. Most primitive types (e.g., integers, floats) are Sync because they are immutable or use atomic operations internally.
Example: A Sync Type
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(5);
let handles: Vec<_> = (0..5).map(|_| {
let data_clone = Arc::clone(&data);
thread::spawn(move || {
println!("{}", *data_clone);
})
}).collect();
for handle in handles {
handle.join().unwrap();
}
}Here, Arc is Sync because multiple threads can hold immutable references to the same value.
Relationship Between Send and Sync
- If a type is
Sync, it means its references areSend. In other words,&TisSendifTisSync. - However, a type can be
Sendbut notSync, meaning it can be transferred between threads but not accessed concurrently.
| What It Ensures | Example Types | |
|---|---|---|
Send | Ownership can move across threads | String, Vec, Arc<Mutex<T>> |
Sync | References can be shared across threads | &i32, Arc<T> |
What If the Arc Is Send But Not Sync?
If the Arc is Send but not Sync, it means the Arc itself can be moved across threads, but references to its contents cannot be shared safely between threads.
It is up to
-
Exclusive Access:
- Only one thread at a time can own the
Arc. - The thread that owns the
Arccan access its contents, but it cannot share references with other threads.
- Only one thread at a time can own the
-
Practical Implications:
- You can transfer ownership of the
Arcbetween threads, but you must ensure that no two threads access the contents concurrently.
- You can transfer ownership of the
-
Example:
- Suppose the inner type is a
Mutex:
- Suppose the inner type is a
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(5));
let handle = thread::spawn(move || {
let mut value = data.lock().unwrap();
*value += 1;
});
handle.join().unwrap();
let value = data.lock().unwrap();
println!("Updated Value: {}", *value);
}Here, the Mutex ensures that even though the Arc is Send but not Sync, access to the data remains safe through locking.