Should beginners use Rc() and Arc() ?
Or it is better to avoid its for now ?
Should beginners use Rc() and Arc() ?
Or it is better to avoid its for now ?
Well, it depends on why you're using them. In general, I would say that Rc<RefCell<..>>
should only be used rarely, but using Rc
on its own is pretty much always perfectly fine. (The difference is that you need RefCell
to modify the shared value.)
Experts should avoid Rc
. Beginners should become experts. Don't worry too much and instead write some code. (Avoid unsafe
far more.)
Using Arc
or Rc
is completely fine; the only problem is that it does come inherently with some restriction (its contents are no longer mutable unless you also use something like RefCell
) which is why you should use Box
whenever the additional capabilities of Rc
aren't needed. So simply always use whatever works best for you and your use-case, and try to figure out / learn how to tell when Rc
is or isn't helping.
Same goes for RefCell
(or Rc<RefCell<…>>
). Sure, there is also the general principle that code with shared mutability is harder to reason about, etc.. (and very minor performance impacts) but also, it's simply a bit less flexible in some ways: you cannot write a &RefCell<Foo<T>> -> &T
function, and to deal with this, using RefCell
often forces more use-cases of Rc
in the contained data (i. e. as a field of Foo<T>
because then you could do &RefCell<Foo<T>> -> Rc<T>
instead, which does work), which in turn might force more usage of RefCell
if the data needs to stay mutable. I. e. it can be somewhat infectious and code becomes more verbose... which is a good reason to avoid using it in cases where it wasn't needed in the first place.
The reason of my asking is this video on yutube..
The Importance of Not Over-Optimizing in Rust
She says:
Beginners, to avoid painful things in rust
a. use owned vars
b. use clone()
c. compile as release
d. use rc() and arc()
I had believing Rc is for experienced devs..
Rc
is definitely used by experts, but the ways in which they are used by experts differ quite a lot from how new Rust users use Rc
.
The issues that new users run into are best described as Rc
/RefCell
hell, and you can tell that you are in this situation by noticing that your codebase is littered with calls to borrow
and borrow_mut
.
Interestingly, Rc
/RefCell
hell pretty much always involves RefCell
too. If you use only Rc
, then you're probably doing something reasonable.
In past I had this opinion:
But seeing this video …
… I wonder if my p.o.v. is a pretty individual thing. I like to find solutions where unnecessary allocations are avoided (e.g. here) and then I sometimes find myself spending hours and hours on finding the optimal type signatures for my problem. A real time killer. It's fun but I see how this lowers my productivity, at least in the short term (though I learn a lot about Rust but this seems endless).
So using Rc
, hmmmm… I would probably only use it where ownership isn't clear, i.e. where a value is "owned" by several structs or locals. Adding
Rc
everywhere might make type signatures also a bit complex and difficult to overlook.
Yes, you should use Arc
.
There are many situations in Rust where Arc
is legitimately needed, no matter whether you're an expert or beginner. For example for sharing data between non-scoped threads, in complex graph structures, or in caches.
Even in situations where Arc
isn't strictly necessary, it's often fine to use it, because it's only a minor performance difference. Using Arc
to reference data is often easier for beginners than using temporary loans (references).
Arc
is fine to use wherever Rc
is, but not the other way, so if in doubt, Arc
is an easier choice.
That depends on your goal. If your goal is to get a deep understanding of the language, then avoiding Rc/Arc
will be more helpful. There are situations where they are legitimately unavoidable, but learning to recognize those takes a lot of time and practice. In short, they are required when single ownership is impossible, but beginners tend to gravitate towards shared ownership when it is not required, or even is plain harmful. This is a consequence of the prevalence of garbage-collected languages, which make it easy to use shared ownership. However, most tasks can be accomplished within Rust's single ownership framework, even if the familiar solutions don't immediately apply.
If your goal is to solve a practical problem, then it is often better to optimize for delivery time, and in that sense Rc/Arc
are indeed very helpful. They basically give you access to a poor man's garbage collector, with all the power it gives you (just note that cycles of references need to be handled manually with strategically placed Weak
pointers, otherwise you will leak resources). This trades the problem of single ownership for a problem of reference cycles, multiple ownership and worse ergonomics, but those problems are generally more familiar to many people. Since Rust is designed around ownership and borrowing, the ergonomics of using reference counting are much worse: you will have to use a lot of ugly Arc<Mutex<T>>
types, explicit cloning, borrowing and locking. This will likely leave a worse impression of the language, and you will be unable to leverage some of its more powerful feature, but as a short-term solution it may be preferable.
The optimum is likely somewhere in the middle. Try to follow the single ownership principles, but if you get in a tight spot it may be better to Arc
and clone
the issue away instead of spending hours or days on the problem. It is important to learn the proper use of the language, but it's also important to maintain your momentum and motivation towards the task and the language. By cloning your issues away you can prevent frustration and focus on the other parts of the language, and there is a lot to learn in Rust.