In this lesson, you will learn how to use Weak pointers to implement the Observer pattern without creating memory-leaking reference cycles.
The Problem: Reference Cycles
In the Observer pattern, a "Subject" (the data source) maintains a list of "Observers" (the listeners). If the Subject holds an Rc<T> to an Observer, and the Observer holds an Rc<T> back to the Subject to stay updated, you have created a reference cycle. Because the reference counts for these objects will never reach zero, the memory will never be freed.
Breaking the Cycle with Weak
To prevent this, the Subject should hold weak references to its Observers. A Weak<T> pointer allows the Subject to point to an Observer without "owning" it or incrementing its strong reference count. This allows the Observer to be dropped naturally when its owner elsewhere in the application discards it.
When the Subject needs to notify an Observer, it must attempt to "upgrade" the Weak<T> to a Rc<T> using .upgrade(). This returns an Option<Rc<T>>. If the result is Some, the Observer is still alive, and we can call its update method. If it is None, we know the Observer has been dropped, and we can safely remove that entry from our list.
Implementation Pattern
use std::rc::{Rc, Weak};
struct Observer { name: String }
struct Subject {
observers: Vec<Weak<Observer>>,
}
impl Subject {
fn notify(&mut self) {
// Retain only those observers that still exist
self.observers.retain(|weak_obs| {
if let Some(obs) = weak_obs.upgrade() {
println!("Notifying: {}", obs.name);
true // Keep in list
} else {
false // Remove dead reference
}
});
}
}
By using Weak pointers, you decouple the lifetime of the Observer from the Subject. The Subject no longer forces the Observers to stay alive, effectively preventing memory leaks while maintaining the necessary "event subscription" link.
Key Takeaway: Always use Weak<T> for back-references in the Observer pattern to prevent reference cycles and allow observers to be dropped automatically.