Course contentsShow
← Rust
Lesson 370 of 1,9104. Lifetimes and Advanced OwnershipFree lesson

Customizing Marker Trait Implementations

Manually declare types as Send or Sync when standard inference fails.

In this lesson, you will learn how to manually override Rust’s automatic thread-safety checks by implementing the Send and Sync marker traits for your own types.

The Role of Auto Traits

As we discussed in the previous lesson, Send and Sync are "marker traits." They don't have any methods; they exist purely to tell the compiler whether your type is safe to move between threads (Send) or share across threads via references (Sync).

Usually, the compiler automatically implements these for you. If a struct is composed entirely of thread-safe types, the struct itself is automatically Send and Sync. However, when you interact with low-level resources—such as raw pointers or types linked to specific thread-local storage—the compiler’s inference engine might incorrectly assume your type is not thread-safe.

Manually Implementing Marker Traits

When you are certain that your type is safe for multi-threaded use, you can opt-in manually. You do this by using an unsafe impl block:

struct MyCustomResource {
    ptr: *mut i32, // Raw pointers are not Send or Sync by default
}

// We have verified that this resource is safe to move between threads.
unsafe impl Send for MyCustomResource {}
unsafe impl Sync for MyCustomResource {}

Why "Unsafe"?

We use the unsafe keyword here because you are effectively "lying" to the compiler’s safety checks. By implementing these traits, you are taking full responsibility for the memory safety of that type across thread boundaries. If you mark a type as Send that actually causes a data race when moved, you have violated Rust’s memory safety guarantees.

You would typically use this when creating wrappers for C libraries, or when working with custom pointers where you have implemented your own synchronization logic (like an internal mutex) that the compiler cannot automatically verify. If your struct contains PhantomData or other complex lifetime arrangements, manual implementation ensures that the compiler treats your custom logic with the thread-safety rules you’ve verified yourself.

Key Takeaway: Manually implementing Send and Sync allows you to override the compiler’s automatic thread-safety inference, provided you accept the responsibility of ensuring the type remains memory-safe across threads.