CodeToLive

Smart Pointers in Rust

Rust provides several smart pointers that manage memory and provide additional functionality.

Box

The Box type is used for heap allocation. It is useful when you need to store data on the heap rather than the stack.


fn main() {
  let boxed_value = Box::new(5);
  println!("Boxed value: {}", boxed_value);
}
      

Rc

The Rc type is used for reference counting. It allows multiple ownership of the same data.


use std::rc::Rc;

fn main() {
  let rc_value = Rc::new(5);
  let cloned_value = Rc::clone(&rc_value);
  println!("Reference count: {}", Rc::strong_count(&rc_value));
}
      

Arc

The Arc type is used for thread-safe reference counting. It is similar to Rc but can be shared across threads.


use std::sync::Arc;
use std::thread;

fn main() {
  let arc_value = Arc::new(5);
  let cloned_value = Arc::clone(&arc_value);

  let handle = thread::spawn(move || {
    println!("Value in thread: {}", cloned_value);
  });

  handle.join().unwrap();
}
      

Interior Mutability with RefCell

The RefCell type allows mutable access to data even when the RefCell itself is immutable. It enforces borrowing rules at runtime.


use std::cell::RefCell;

fn main() {
  let value = RefCell::new(5);
  *value.borrow_mut() += 1;
  println!("Value: {}", value.borrow());
}
      

Combining Smart Pointers

Smart pointers can be combined to achieve more complex behavior. For example, Rc<RefCell<T>> allows multiple ownership with interior mutability.


use std::rc::Rc;
use std::cell::RefCell;

fn main() {
  let value = Rc::new(RefCell::new(5));
  let cloned_value = Rc::clone(&value);
  *cloned_value.borrow_mut() += 1;
  println!("Value: {}", value.borrow());
}
      

Custom Smart Pointers

You can create custom smart pointers by implementing the Deref and Drop traits.


use std::ops::Deref;
use std::ops::Drop;

struct MyBox<T>(T);

impl<T> MyBox<T> {
  fn new(x: T) -> MyBox<T> {
    MyBox(x)
  }
}

impl<T> Deref for MyBox<T> {
  type Target = T;

  fn deref(&self) -> &T {
    &self.0
  }
}

impl<T> Drop for MyBox<T> {
  fn drop(&mut self) {
    println!("Dropping MyBox!");
  }
}

fn main() {
  let my_box = MyBox::new(5);
  println!("Value: {}", *my_box);
}
      

Best Practices

Real-World Use Cases

Smart pointers are commonly used in scenarios like:

Next: Pattern Matching in Rust