Rust Guide > Documentation > Collections > Weak

Introduction

The Weak class in Rust is a part of the standard library that provides a non-owning reference to data managed by Arc (Atomic Reference Counting). This is useful to prevent reference cycles in situations where you have cyclic dependencies between Arc pointers. A Weak reference does not keep the data alive and must be upgraded to an Arc to access the data.

Using Weak

The Weak class can be used to create a non-owning reference to an Arc-managed piece of data. Here is a basic example:

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak = Arc::downgrade(&strong);

    // Upgrade the Weak reference to an Arc
    if let Some(strong_again) = weak.upgrade() {
        println!("Upgraded value: {}", *strong_again);
    } else {
        println!("The value has been dropped");
    }
}
// Output: Upgraded value: 5

Key Methods

Below are some of the key methods exposed by the Weak class:

downgrade

Creates a Weak reference from an Arc.

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak = Arc::downgrade(&strong);
    println!("Weak reference created");
}
// Output: Weak reference created

upgrade

Attempts to upgrade the Weak reference to an Arc. Returns Some if the data is still alive, otherwise returns None.

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak = Arc::downgrade(&strong);

    if let Some(strong_again) = weak.upgrade() {
        println!("Upgraded value: {}", *strong_again);
    } else {
        println!("The value has been dropped");
    }
}
// Output: Upgraded value: 5

strong_count

Returns the number of strong (i.e., Arc) references.

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak = Arc::downgrade(&strong);
    println!("Strong count: {}", Arc::strong_count(&strong));
}
// Output: Strong count: 1

weak_count

Returns the number of Weak references.

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak = Arc::downgrade(&strong);
    println!("Weak count: {}", Arc::weak_count(&strong));
}
// Output: Weak count: 1

ptr_eq

Checks if two Weak references point to the same allocation in the heap.

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak1 = Arc::downgrade(&strong);
    let weak2 = Arc::downgrade(&strong);
    println!("Weak references equal: {}", Weak::ptr_eq(&weak1, &weak2));
}
// Output: Weak references equal: true

Example Usage

Example 1: Basic Usage

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak = Arc::downgrade(&strong);

    if let Some(strong_again) = weak.upgrade() {
        println!("Upgraded value: {}", *strong_again);
    } else {
        println!("The value has been dropped");
    }
}
// Output: Upgraded value: 5

Example 2: Preventing Cyclic References

use std::sync::{Arc, Weak};
use std::cell::RefCell;

struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Arc<Node>>>,
}

fn main() {
    let leaf = Arc::new(Node {
        value: 3,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![]),
    });

    let root = Arc::new(Node {
        value: 1,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![Arc::clone(&leaf)]),
    });

    *leaf.parent.borrow_mut() = Arc::downgrade(&root);

    println!("Leaf parent: {:?}", leaf.parent.borrow().upgrade().map(|n| n.value));
}
// Output: Leaf parent: Some(1)

Example 3: Checking Strong and Weak Counts

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak = Arc::downgrade(&strong);
    println!("Strong count: {}", Arc::strong_count(&strong));
    println!("Weak count: {}", Arc::weak_count(&strong));
}
// Output:
// Strong count: 1
// Weak count: 1

Example 4: Upgrading a Weak Reference

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak = Arc::downgrade(&strong);

    if let Some(strong_again) = weak.upgrade() {
        println!("Upgraded value: {}", *strong_again);
    } else {
        println!("The value has been dropped");
    }
}
// Output: Upgraded value: 5

Example 5: Checking Pointer Equality

use std::sync::{Arc, Weak};

fn main() {
    let strong = Arc::new(5);
    let weak1 = Arc::downgrade(&strong);
    let weak2 = Arc::downgrade(&strong);
    println!("Weak references equal: {}", Weak::ptr_eq(&weak1, &weak2));
}
// Output: Weak references equal: true

Considerations

  • Weak references do not keep the data alive. If all Arc references are dropped, upgrade will return None.
  • Using Weak is essential to prevent reference cycles, which can cause memory leaks.
  • When using Weak in multi-threaded contexts, ensure proper synchronization as upgrading a Weak reference can return None if the last Arc is dropped.

See Also

  • Arc - An atomic reference-counted smart pointer for shared ownership across threads.
  • Rc - A reference-counted smart pointer for single-threaded scenarios.
  • Mutex - A mutual exclusion primitive useful for protecting shared data across threads.
  • RwLock - A reader-writer lock for allowing concurrent reads or exclusive writes across threads.