Keeping the allocation alive without the object sounds exactly like keeping a Weak before try_unwrap.
On that topis, could one reuse a Weak that has no current strong owners?
impl Weak<T> {
/// Reuse the allocation if there are no strong references to it.
pub fn try_init(&self, val: T) -> Result<Rc<T>, T> {
// or `Option<Rc<T>>` but avoids the drop of `T`
…
}
}
It kind of is an argument-from-authority, but in any case, I think the question can be pretty easily answered.
The problem is that it's not as simple as "RC is good or bad". The OP said that it can be quite efficient, not that it necessarily is efficient, or efficient in all use cases.
Well-written Rust and C++ use reference counting selectively, both by allowing you to allocate objects with no reference count at all, and by manually eliding unnecessary bumps even when the object itself does have a reference count. In C#, on the other hand, all class instances are implicitly garbage collected, so if C# was using reference counting, it would be ubiquitous reference counting. Taking the existing C# design verbatim, but replacing the tracing GC with atomic RC, would probably make it slower (it would spend a bunch more time messing around with the reference counts). Taking the existing Rust design, but replacing Rc with a tracing garbage collector, would probably also make it slower (since it would no longer have deterministic destruction, and would have to be able to trace through all objects, a bunch of weird optimization shenanigans would suddenly become illegal).
All optimization is optimization to a niche, and Rust and C# target different niches.