Skip to main content

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 be Send and how it enables safe cross-thread data transfer.
  • The Sync Trait: What it means for a type to be Sync and how it enables safe cross-thread data access.
  • Implementing Send and Sync: 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 is Send if it's safe to move to another thread.
  • Sync: A type is Sync 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 not Send or Sync.
  • RefCell<T> is not Sync.
  • Raw pointers are not Send or Sync.

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:

  1. Define a struct that holds a raw pointer.
  2. Use an unsafe block to implement Send and Sync for the struct.

Walkthrough:

  • By implementing Send and Sync, 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 and Sync for your types whenever possible.
  • And this: Only implement Send and Sync 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 and Sync are marker traits that ensure thread safety.
  • Most types are Send and Sync by default.
  • You can implement Send and Sync manually, but it's unsafe.

➡️ 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.


Further Reading (Rust Resources)