Why RefCell can not be send between threads safely?

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:

1 Like

Use an implementation of RwLock instead.

2 Likes

Arc<T> is not Send if T is not Sync, so this is probably not the reason OP is looking for.

2 Likes

Um yes, my bad. I rush replied without too much thinking. Because RefCell<T> actually impls Send (if T impl Send) (Doc)

2 Likes

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 :slight_smile:

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.

12 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.