I can understand why a RefCell
does not implement Sync
, because it is designed to be mutably borrowed. But I can not understand why it does not implement Send
. In my understanding, Send
is just to transport owership, thread1
can get the owership of it, and thread2
also can get the owership of it, why it can not be sent between threads?
If I need RefCell
to be sent to other threads, what should I do?
Because you can create Arc<RefCell<T>>
, which is effectively sharing RefCell<T>
between threads.
Edit:
Use an implementation of RwLock
instead.
Arc<T>
is not Send
if T
is not Sync
, so this is probably not the reason OP is looking for.
Um yes, my bad. I rush replied without too much thinking. Because RefCell<T>
actually impls Send
(if T
impl Send
) (Doc)
There’s a lot of inaccuracies in your question.
This is not the reason why RefCell
is not Sync
, and also a weird way of describing RefCell
. A RefCell
’s main point is not to mutably borrow the whole RefCell
; instead, it’s a so-called “interior-mutability primitive”, which means that it allows mutable access to the interior from an “immutable” (aka. a shared) borrow of the exterior, i.e. you can get &mut T
access from a &RefCell<T>
reference.
For soundness, this kind of access then involves a run-time check to ensure that Rust’s borrowing rules are still not violated, i.e. at run-time, the implementation of RefCell
ensures that the contained value is always (at any point in time) only borrowed either at most once mutably, or exclusively immutably (but as often in parallel as you like).
Thus far, this has nothing to do with Send
/Sync
though. The reason why RefCell
is not Sync
is really really simple: its implementation of the logic that ensures Rust’s borrowing rules at run-time is simple and efficient but not thread-safe. This is similar to why Rc
is not Sync
. If thread-safety is required, of course there are alternatives. For Rc
that’s Arc
; for RefCell
, the most similar thread-safe alternative is RwLock
. (If you know the type, you might be aware that it includes locking & blocking behavior, whereas RefCell
never locks but panics instead. The reason why RefCell
never locks is that it would simply be useless to lock; the single-threaded access to RefCell
means that every lock would immediately be a deadlock, so it’s better to just panic.)
As @zirconium-n also already noted, RefCell
does implement Send
. If you somehow determined the opposite, you should probably review how you drew that (incorrect) conclusion, so you can learn to make more precise observations about trait implementations on types. If you just look into the docs, it’s hard to interpret the Send
implementation wrong. Perhaps there was some code you tried that you believed doesn’t compile because RefCell
isn’t Send
? Feel free to post such code if you want to learn more about the true requirements / reasons it didn’t compile
This understanding sounds correct to me.
The remainder of your question after that is irrelevant because it is formulated based on the assumption that RefCell
doesn’t implement Send
.
Of course the Send
implementation of RefCell
, if expressed more precisely, wouldn’t just be described as “RefCell
implements Send
”, but more like “for types T
, the type RefCell<T>
implements Send
(precisely) whenever T
itself implements Send
”. If you’re unsure how to interpret such generic implementations or why the requirement that T
must implement Send
in order for RefCell<T>
to implement Send
is necessary, feel free to ask follow-up questions.