Course contentsShow
← Rust
Lesson 804 of 1,9106. Generics and Static DispatchFree lesson

Generic Type Safety in Multithreaded Apps

Ensure generic data structures propagate thread-safety markers to their underlying components.

In this lesson, you will learn how to ensure that your generic data structures automatically inherit the thread-safety guarantees of the types they contain.

When you design a generic container—like a Buffer<T>—you need to consider whether it is safe to send that container across thread boundaries. In Rust, this is managed by the Send and Sync marker traits. If you don't explicitly handle these, the compiler might prevent you from using your container in multithreaded contexts, even if the underlying data is perfectly safe.

The Propagation Problem

By default, the compiler calculates whether a generic struct is Send or Sync based on its fields. If a field is T, the compiler assumes your struct is Send only if T is Send. This is usually exactly what you want! However, if you add complex internal structures (like raw pointers or non-thread-safe handles), you risk "poisoning" your generic type, making it unusable in multithreaded code.

Consider this simple generic wrapper:

struct SafeContainer<T> {
    item: T,
}

// The compiler automatically infers:
// SafeContainer<T> is Send if T is Send
// SafeContainer<T> is Sync if T is Sync

If you add a field that isn't Send, such as std::rc::Rc<T>, the entire SafeContainer<Rc<T>> will no longer be Send. The generic structure effectively "contracts" a virus of non-thread-safety from its components.

Maintaining Bounds

If you are writing a library, you might want to enforce that your generic structure only works with thread-safe types. You can do this by adding trait bounds to your struct definition:

struct ThreadSafeBox<T: Send + Sync> {
    data: T,
}

By placing these bounds on the struct, you ensure that any attempt to put a non-thread-safe type into your container results in a compile-time error at the point of instantiation, rather than a cryptic error deep inside your multithreaded logic. This is the hallmark of Rust’s "type-driven" safety: it prevents concurrency bugs by proving your data usage is safe before the code even runs.

Key Takeaway: Generic structures propagate thread-safety markers from their inner types automatically, but you can explicitly enforce Send and Sync bounds on your structs to guarantee thread safety at the point of design.