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
SendTrait: What it means for a type to beSendand how it enables safe cross-thread data transfer. - ✅ The
SyncTrait: What it means for a type to beSyncand how it enables safe cross-thread data access. - ✅ Implementing
SendandSync: 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 isSendif it's safe to move to another thread.Sync: A type isSyncif 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 notSendorSync.RefCell<T>is notSync.- Raw pointers are not
SendorSync.
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
unsafeblock to implementSendandSyncfor the struct.
Walkthrough:
- By implementing
SendandSync, 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
SendandSyncfor your types whenever possible. - And this: Only implement
SendandSyncmanually 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:
SendandSyncare marker traits that ensure thread safety.- Most types are
SendandSyncby default. - You can implement
SendandSyncmanually, 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.