Course contentsShow
← Rust
Lesson 1145 of 1,91010. Concurrency PrimitivesFree lesson

The Send/Sync Contract in Libraries

You will learn how to design public-facing interfaces that clearly communicate thread-safety guarantees.

In this lesson, you will learn how to design public-facing library interfaces that communicate thread-safety guarantees through the Send and Sync marker traits.

When you build a library, your types exist in a "contract" with the user. If you are building a data structure or a service handler, you aren't just writing code; you are making a promise to the compiler about whether your type is safe to move across thread boundaries (Send) or be accessed concurrently through a shared reference (Sync).

Designing the Interface

If you don't explicitly implement these traits, Rust’s compiler will automatically derive them for you based on your fields. However, in complex concurrent systems—especially those involving UnsafeCell or manual memory management—you often need to manually control this contract.

Consider a library component that manages an internal buffer:

pub struct ConcurrentBuffer {
    buffer: *mut u8, // Internal raw pointer
}

// By default, raw pointers are !Send and !Sync.
// If we verify the internal logic is thread-safe, we must 
// explicitly state this in our public API:
unsafe impl Send for ConcurrentBuffer {}
unsafe impl Sync for ConcurrentBuffer {}

Why the Contract Matters

When a user imports your library, they rely on these markers to write their own safe code. If you accidentally mark a non-thread-safe struct as Sync, you invite data races into the user's application, which is a violation of Rust’s core safety guarantees.

Conversely, being too restrictive (e.g., marking a perfectly safe struct as !Send) forces users to wrap their data in mutexes unnecessarily, killing performance and ergonomics.

To design a robust library, always:

  1. Audit your internals: If you use UnsafeCell or custom primitives, ensure your trait implementations match your safety invariants.
  2. Document your choice: If your type is !Send or !Sync by design, document it clearly. If you override the compiler's default inference, include a // Safety: comment explaining why the implementation is valid for the consumer.

The Send and Sync traits act as the "legal documentation" of your library’s concurrency architecture.

Key Takeaway: The Send and Sync markers are the most critical part of your public API contract; they define whether your library empowers or endangers the thread-safety of the systems that consume it.