Why rust Arc has a weak reference

Hmmm ... note however (if I understand correctly) that the cache items still do occupy memory even after being evicted from slots (and not referred to from outside). (It seems to me that the Weaks in map stay there forever.)

That's because while the Rc's referent gets dropped (its destructor called) when the strong refcount reaches zero, but memory gets deallocated only after both string and weak refcounts reach zero.

That's because refcounts and payload are all stored within the same allocation.

There may be another implementations of refcounted pointer that do not suffer from this, but right now the std one behaves like this. (It is also mentioned in docs.)

1 Like

When both strong and weak references reach 0, the data will be released. I heard this from Bing AI, but in practice, it is difficult for me to obtain a situation where the strong reference is 0 and the weak reference is not 0, so I cannot verify it. However, most sources online say that data is released only when the strong reference is 0.

It's trivial, actually:

let rc = Rc::new(String::from("text"));
let weak = Rc::downgrade(&rc);
drop(rc);

After this, weak will be the only reference to place where String was, but the String itself is already dropped.

2 Likes

I tested it, and it seems that when the strong reference is 0 and the weak reference is 1, it is still released. Therefore, the RC smart pointer releases the data only when the strong reference is 0, and the weak reference has no effect.

The destructor is run when the strong count reaches zero, but that does not mean that the underlying memory is immediately freed. If you have a weak count bigger than zero, it is guaranteed not to be freed till the weak count also reaches zero. From the docs:

The downgrade method can be used to create a non-owning Weak pointer. A Weak pointer can be upgraded to an Rc, but this will return None if the value stored in the allocation has already been dropped. In other words, Weak pointers do not keep the value inside the allocation alive; however, they do keep the allocation (the backing store for the inner value) alive.

1 Like

they do keep the allocation (the backing store for the inner value) alive.
what is this . backing store?

keeping the allocation alive?

When you create Rc, you create an allocation on the heap for the following struct:

struct RcBox<T> {
    strong: Cell<usize>,
    weak: Cell<usize>,
    value: T,
}

(for Arc you just replace Cell<usize> with AtomicUsize). Then, Rc<T> contains a pointer to this allocation.

When you drop the last Rc, i.e. strong falls to 0, destructor calls drop_in_place on value.

When you drop the last pointer, i.e. both strong and weak fall to 0, memory holding RcBox is freed.

4 Likes

Being excessively pedantic here, only strong references can be used to access data; weak references can be upgraded to strong references if and only if there is at least one strong reference to the data at the time you request an upgrade. This means that if someone else has already destroyed the data, you're unable to upgrade, and have to handle this sensibly.

1 Like

Correct, when the strong reference count reaches 0, the data of type T is dropped. When both the strong and weak reference counts reach 0, the memory holding the RcBox is freed. Therefore, the Rc struct itself is also released.

1 Like