19.8 Weak<T>: Non-Owning References

While Rc<T> and Arc<T> handle shared ownership effectively, they can inadvertently form reference cycles if two objects reference each other strongly. Such cycles prevent the reference count from reaching zero, causing memory leaks.

Weak<T> provides a non-owning pointer solution. Converting an Rc<T> or Arc<T> into a Weak<T> (using Rc::downgrade or Arc::downgrade) lets you reference data without increasing the strong count. This breaks potential cycles because a Weak<T> doesn’t keep data alive by itself.

19.8.1 Strong vs. Weak References

  • Strong Reference (Rc<T> / Arc<T>): Contributes to the reference count. Data remains alive while at least one strong reference exists.
  • Weak Reference (Weak<T>): Does not increment the strong reference count. If all strong references are dropped, the data is deallocated, and any Weak<T> pointing to it will yield None when upgraded.

19.8.2 Example: Avoiding Cycles

use std::cell::RefCell;
use std::rc::{Rc, Weak};

#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Option<Weak<RefCell<Node>>>>,
    children: RefCell<Vec<Rc<RefCell<Node>>>>,
}

fn main() {
    let parent = Rc::new(RefCell::new(Node {
        value: 1,
        parent: RefCell::new(None),
        children: RefCell::new(vec![]),
    }));
    let child = Rc::new(RefCell::new(Node {
        value: 2,
        parent: RefCell::new(Some(Rc::downgrade(&parent))),
        children: RefCell::new(vec![]),
    }));
    parent.borrow_mut().children.borrow_mut().push(Rc::clone(&child));
    println!("Parent: {:?}", parent);
    println!("Child: {:?}", child);
    // No reference cycle occurs because the child holds only a Weak link to its parent.
}

19.8.3 Upgrading from Weak<T>

To access the data, you attempt to “upgrade” a Weak<T> back into an Rc<T> or Arc<T>. If the data is still alive, you get Some(...); if it has been dropped, you get None.