I don't know why we need Rc (P/s All code provided was very interesting. ! look at comment)

See on godbolt : there are 2 instance.
Rc is more complicated than pure ref (&)

Example when I use 2 Rc with 1 ref and 5 ref

Rc is used for shared ownership, i.e. when we need multiple place independently keeping some object alive for as long as they need.

Plain references are used for shared borrowing, i.e. when we need one place to keep object alive while other places look into it for a strictly limited time.

what’s applications ? I only use it with refcell. when I need more than one modifiable ref

rc even doesn’t work with multi thread(compare with arc)

What are applications of single-threaded shared ownership?

I'm curious about this too. Can anyone share an example where Rc is a must-have?

Well, strictly speaking, it is never must-have, since Arc is strictly more powerful and only slightly less performant is some specific cases. But if you're doing something with concurrency but not parallelism (i.e. several tasks on a single thread), you don't need Arc's thread-safety, but you still need these tasks to collectively own their common data - hence, wrap it in Rc.

That's what I thought as well. Some embedded setup or microservice where a single core is at the disposal. Using Rc to share immutable pointers between tasks.

I would assume that Rc (or Arc) is needed for spatial data structures, e.g. a Delaunay triangulation, where multiple edges can own the same vertex node.

You can always fairly easily share data in other ways, but there's no reason to make things harder than you need to. I've commonly used a parsed on startup Config that can be shared between different parts of the program with Rc<Config> for example, it's the simplest way to avoid adding a lifetime parameter or cloning the data - the first breaks a million different little things and the second is pointlessly expensive.

Makes sense. Shared nodes in graphs.

OpenGL or any other similar library where UI requests have to come from one specific thread.

It's much easier to guarantee that you wouldn't send your callback to some other thread when compiler does that and not you.

Granted it's not the best choice and we have Vulkan that lifts that requirement… but one is not always have the luxury of picking environment, otherwise we wouldn't have had crazy jenga tower of async-on-top-of-threads, too.

Here is a scenario...

When readonly data will be provided to several closures that will have independent lifetimes, each closure requires ownership of their data. Though each can be given a clone it is widely more efficient to wrap the data in an Rc and give a clone of that.

& is not a general-purpose reference. It's a temporary scope-limited permission to view data already stored somewhere else.

Rc is not temporary. Rc is not limited to only live in a statically-known scope. Rc stores data.

Tested and trued. Heap clone increases the time of generating closures by a factor of 3x for 3 closures with a huge string here:

I don't mention 3x the memory needed for clone version.

Also did the same test with criterion on my local. The ratio of mean execution time is 3x there as well.
It grows with every additional closure.

Rc here is close to mandatory.

Interesting test! So, borrowing a local variable can’t escape the function, which means we need to clone it — but cloning can hurt performance.
Using Rc solves this problem nicely.
Very interesting! Thank you so much.
test

This example does not answer original question.
"Rc is more complicated than pure ref (&)"

So, you should compare & with Rc, not cloning with Rc.

I have changed your code to use & in place of cloning

And results have changed a lot :slight_smile:

--- Rc version ---
Rc version finished in: 11.947693ms
Closure 1 sees: mother_of_
Closure 2 sees: mother_of_
Closure 3 sees: mother_of_
--- Cloned version ---
Cloned version finished in: 30ns
Closure 1 sees: mother_of_
Closure 2 sees: mother_of_
Closure 3 sees: mother_of_

Now & is 398 000x faster than Rc

Rust have 3 main referencies, and here are more derivatives like Cell, RefCell, RwLock and so on.

  1. & - Reference. Single owner reference, have created data, owns data and must delete data after it comes out of scope. This means owner must live longes off all references to his data. And data can be both on stack or heap. To use this reference you must prove to compiler that owner of data will live long enough for all references to data to be deleted. If you can't prove this you can't use this reference type.

  2. Rc - Reference counter. Reference with a counter to count how many variables reference this data. Data is on heap and everyone who have reference is owner. Last owner must delete data, but others can't. Original creator of data can give Rc to others and go out of scope withour deleting data, because he don't own data. You don't need to prove compiler that data will live long enough, because everyone is owner of data. This can't be used in multi threaded apps.

  3. Arc - Atomic reference counter. Same as Rc, but can be used in multi threaded apps.

& is most efficient reference, but most limited reference. And you often may need to use lifetimes to use it. If you don't want to add lifetimes all around your code you will need to use Rc, like in simonbuchan message Rc
Or Delaunay triangulation where every triangle needs to own point. Here can't be only single owner. You will need to use Rc. But if your app is multi threaded, you will need to use Arc.

You're right. The customer is always right. Perhaps dynamically stored closures with 'static lifetimes that can be changed at runtime can demonstrate the case better?

I don't even know meaning of your new added code, but it is not comparing & with Rc. It compares clone() with Rc. Which don't even need to be compared to be known which one is faster. Creating 64 bit of data, or 1 Mb of data.

Updated your code to not compare speed of cloning 64 bit of data to speed of cloning 1 Mb of data. P.s. Random number for data, because I am to lazy to check real numbers. :slight_smile:

Result

--- Rc version ---
Rc version finished in: 120ns
Closure 1 sees: mother_of_
Closure 2 sees: mother_of_
Closure 3 sees: mother_of_
--- Cloned version ---
Cloned version finished in: 80ns
Closure 1 sees: mother_of_
Closure 2 sees: mother_of_
Closure 3 sees: mother_of_