3.2 KiB
Extensible Concurrency with the Sync
and Send
Traits
The Rust language gas very few concurrency features.
Almost every feature we talked about so far has been part of the std library and not the language.
Your options for handling concurrency are not limited to the language or the std library; you are able to write your own concurrency features or use those written by others.
However, tow concurrency concepts that are embedded into the language are the std::marker
traits Sync
and Send
.
Allowing Transference of Ownership Between Threads with Send
The marker trait Send
indicates that ownership of values of the type implemented Send
can be transferred between threads.
Almost every Rust type is Send
.
There are some exceptions, including Rc<T>
.
This cannot be be Send
because if you cloned a Rc<T>
value and tried to transfer ownership of the clone to another thread, both threads might update the reference count at the same time.
This is the reason, Rc<T>
is implemented for use in single-threaded situations where you don't want to pay the thread-safe performance penalty.
Rust's type system and trait bounds ensure that you can never accidentally send a Rc<T>
value across threads unsafely.
When we tried to do this before we got the error: the trait Send
is not implemented for Rc<Mutex<i32>>
.
Any type composed entirely of Send
types is automatically marked as Send
as well.
Almost all primitives are Send
, aside form raw pointers (This will be discussed in ch20).
Allowing access form Multiple Threads with Sync
The Sync
marker trait indicates that it is safe for the type implementing Sync
to be referenced from multiple threads.
Any type T
is Sync
if &T
(an immutable reference to T
) is Send
, meaning the reference can be sent safely to another thread.
Similar to Send
, primitive types are Sync
and types composed entirely of types that are Sync
are also Sync
.
The smart pointer Rc<T>
is not Sync
as well for the same reasons that it is not Send
.
The RefCell<T>
types (Ch15) and the family related to Cell<T>
types are not Sync
.
The implementation for borrow checking that RefCell<T>
does at runtime is not thread-safe.
The smart pointer Mutex<T>
is Sync
and can be used to share access with multiple threads alike what we saw in "Sharing a Mutex<T>
Between Multiple Threads" Section.
Implementing Send
and Sync
Manually is Unsafe
Because types that are made up of Send
and Sync
traits are also automatically Send
and Sync
, we don't need to implement these traits manually.
As marker traits they don't even have any methods to implement.
These are just useful for enforcing invariants related to concurrency.
Manually implemented these traits involves implementing unsafe Rust code (This will be discussed in Ch20).
For now the important information is that building new concurrent types not made up of Send
and Sync
parts requires careful thought to uphold the safety guarantees.
"The Rustonomicion" has more information about these guarantees and how to uphold them.