Sync and Send Traits: Extensible Concurrency
After learning about Shared-State Concurrency, it's time to understand the magic behind Rust's thread safety: the Sync
and Send
Traits.
📚 Prerequisites
- A deep understanding of Rust's concurrency model, including threads, message passing, and shared-state concurrency.
- Familiarity with Rust's trait system.
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ The
Send
Trait: What it means for a type to beSend
and how it enables safe cross-thread data transfer. - ✅ The
Sync
Trait: What it means for a type to beSync
and how it enables safe cross-thread data access. - ✅ Implementing
Send
andSync
: How to manually implement these traits for your own types (when it's safe to do so).
🧠 Section 1: The Core Concepts of Send
and Sync
The Send
and Sync
traits are two of the most important concepts in Rust's concurrency story. They are marker traits, which means they don't have any methods. They just "mark" a type as having certain properties.
Key Principles:
Send
: A type isSend
if it's safe to move to another thread.Sync
: A type isSync
if it's safe to share between threads (i.e., multiple threads can have references to it).
💻 Section 2: Deep Dive - How Send
and Sync
Work
Most primitive types in Rust are both Send
and Sync
. A type composed entirely of Send
and Sync
types is also Send
and Sync
.
Rc<T>
is notSend
orSync
.RefCell<T>
is notSync
.- Raw pointers are not
Send
orSync
.
The compiler enforces these rules at compile time, which is a huge part of what makes Rust's concurrency "fearless".
🛠️ Section 3: Project-Based Example: Implementing Send
and Sync
Manually
You can manually implement Send
and Sync
for your own types if you can guarantee that they are safe to use in a concurrent context. This is an unsafe
operation because the compiler can't verify your guarantees.
// main.rs
struct MyBox(*mut u8);
unsafe impl Send for MyBox {}
unsafe impl Sync for MyBox {}
fn main() {
// ...
}
The Goal:
To create a custom type that wraps a raw pointer and mark it as Send
and Sync
.
The Plan:
- Define a struct that holds a raw pointer.
- Use an
unsafe
block to implementSend
andSync
for the struct.
Walkthrough:
- By implementing
Send
andSync
, we are telling the compiler that we have manually verified that our type is safe to use in a concurrent context.
✨ Section 6: Best Practices and Anti-Patterns in Rust
Rust Best Practices (Idiomatic Way):
- Do this: Rely on the compiler to automatically implement
Send
andSync
for your types whenever possible. - And this: Only implement
Send
andSync
manually when you are absolutely sure it's safe to do so.
💡 Conclusion & Key Takeaways
You've learned about the Send
and Sync
traits, which are the cornerstone of Rust's fearless concurrency.
Let's summarize the key Rust takeaways:
Send
andSync
are marker traits that ensure thread safety.- Most types are
Send
andSync
by default. - You can implement
Send
andSync
manually, but it'sunsafe
.
➡️ Next Steps
In the next article, "Async/Await in Rust: An Introduction", we'll explore a different model of concurrency that is becoming increasingly popular.