Smart Pointers
Weak<T>
Weak<T> (std::rc::Weak<T> for single-threaded code and std::sync::Weak<T> for multi-threaded code) is a non-owning smart pointer that references an Rc<T> or Arc<T> allocation without preventing the inner value T from being dropped.
Strong vs. Weak Ownership Semantics
An Rc<T> or Arc<T> represents strong ownership: as long as strong_count >= 1, the inner value T stays alive. A Weak<T> represents contingent access (observation or back-reference):
- Creating a
Weak<T>viaRc::downgrade(&rc)orArc::downgrade(&arc)incrementsweak_count, leavingstrong_countunchanged. Weak<T>does not implementDerefbecause the targetTmay have already been dropped.- To read the value, callers must call
.upgrade(), which returnsOption<Rc<T>>(orOption<Arc<T>>):- If
strong_count >= 1,.upgrade()incrementsstrong_countand returnsSome(rc). - If
strong_count == 0,.upgrade()returnsNone.
- If
rc1: Rc<T> (strong_count = 1)
|
| Rc::downgrade(&rc1)
v
weak: Weak<T> (weak_count = 1)
|
+---> While rc1 is alive: weak.upgrade() -> Some(Rc<T>) (strong_count becomes 2)
+---> After rc1 is dropped: weak.upgrade() -> None
use std::rc::{Rc, Weak};
fn main() {
let strong: Rc<String> = Rc::new(String::from("ephemeral cache entry"));
let weak: Weak<String> = Rc::downgrade(&strong);
if let Some(upgraded) = weak.upgrade() {
println!(
"while strong exists -> value: {:?}, strong_count: {}",
upgraded,
Rc::strong_count(&upgraded)
);
}
// Drop the sole strong owner:
drop(strong);
println!("after drop -> upgrade(): {:?}", weak.upgrade());
}
while strong exists -> value: "ephemeral cache entry", strong_count: 2
after drop -> upgrade(): None
Core Weak<T> Methods
| Method | Signature (rc::Weak / sync::Weak) |
Behavior |
|---|---|---|
Weak::new() |
const fn new() -> Weak<T> |
Creates a dangling Weak<T> without allocating on the heap (upgrade() always returns None) |
Rc::downgrade(&rc) |
fn downgrade(this: &Rc<T>) -> Weak<T> |
Creates a Weak<T> pointing to this's allocation |
.upgrade() |
fn upgrade(&self) -> Option<Rc<T>> |
Attempts to promote Weak<T> into a strong Rc<T> / Arc<T> |
.strong_count() |
fn strong_count(&self) -> usize |
Returns the number of live strong pointers (0 if value was dropped) |
.weak_count() |
fn weak_count(&self) -> usize |
Returns the number of live weak pointers (0 if no strong pointers remain or dangling) |
.ptr_eq(&other) |
fn ptr_eq(&self, other: &Weak<T>) -> bool |
Returns true if both Weak<T> pointers refer to the same allocation |
Self-Referential Initialization with Rc::new_cyclic and Arc::new_cyclic
Rc::new_cyclic and Arc::new_cyclic pass a &Weak<T> to a constructor closure before the T value finishes initializing. Once the closure returns T, the Weak<T> becomes upgradable, allowing a struct or graph node to store a non-owning weak reference to itself or to child nodes created during construction without needing an Option wrapper:
use std::rc::{Rc, Weak};
struct Gadget {
id: u32,
me: Weak<Gadget>,
}
impl Gadget {
fn new(id: u32) -> Rc<Self> {
Rc::new_cyclic(|weak_self| Gadget {
id,
me: Weak::clone(weak_self),
})
}
fn identify(&self) -> u32 {
self.me.upgrade().unwrap().id
}
}
fn main() {
let gadget = Gadget::new(42);
println!("gadget id via self-weak: {}", gadget.identify());
println!(
"strong_count: {}, weak_count: {}",
Rc::strong_count(&gadget),
Rc::weak_count(&gadget)
);
}
gadget id via self-weak: 42
strong_count: 1, weak_count: 1