Skip to content

Instantly share code, notes, and snippets.

@s4hubhamp
Last active March 5, 2025 07:22
Show Gist options
  • Save s4hubhamp/046ea9c7e5f0964ac19c3cf70c5f161f to your computer and use it in GitHub Desktop.
Save s4hubhamp/046ea9c7e5f0964ac19c3cf70c5f161f to your computer and use it in GitHub Desktop.
Rust Boxes and Cells
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use std::ops::Deref;
fn boxes() {
// Box allows mutable and immutable borrows to a variable. It's just a pointer to data on a heap
// Box returns ownership of the passed value. So if passed value does not implement clone then value gets moved.
let a = Box::new(1); // a is a Box pointing to an integer on the heap
let b = &a; // b is a reference to the Box
// :p prints the address that a variable holds
println!("Address of data on the heap for a: {:p}, {:p}", a, &*a);
// below b holds address of a, so it prints address of a. Which will be a stack address
println!("Address of a: {:p}, {:p}", b, &a);
// when we print b, behind the scenes rust dereferences multiple times to reach to the value on heap
println!("B is {}, {}, {}", *(*b), b, (b.deref()).deref());
// moving the value from stack to heap
let stack_val = String::from("Redford");
let heap_val = Box::new(stack_val); // note that the move occurrs here and we can't use stack_val after passing it to Box::new
println!("heap_val is {:?}", heap_val);
// moving value from heap to stack
let heap_val = Box::new(String::from("Rufus"));
let stack_val = *heap_val;
println!("stack_val: {:?}", stack_val); // move occurrs and now we can't use heap_val
}
fn rc() {
// Rc is just like box in that it stores data on heap but it allows multiple owners to the same data on heap
// multiple owners are safe because data inside it is immutable and the data on heap will be freed once
// all the owners are done using it (strong count becomes zero)
let mut a = Rc::new(vec![1.0, 2.0, 3.0]);
// a and b both point to the same memory location. Shared Ownership of a data on heap
let mut b = Rc::clone(&a);
// we cannot modify the data inside the Rc
// b.push(4.0);
// whereas if we use box as below only a points to heap data and b points to a. A is the exclusive owner of data
let mut a = Box::new(vec![1]);
let b = &mut a;
// we can modify values inside of the box
b.push(2);
// Why can't we modify the data inside the Rc?
// multiple mutable borrows to the same place can cause data races and inconsistencies.
// Note that for `Box` you can only have one mutable reference at a time so it's fine to modify the value.
// When to use rc? Why it exists if instead i can have multiple immutable references?
// References are valid until the main object which is being refferred(owner) is available. But in some cases owner will not
// live long enough. This can be best understood by examples, https://doc.rust-lang.org/std/rc/index.html#examples
// https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#having-multiple-owners-of-mutable-data-by-combining-rct-and-refcellt
// https://dhghomon.github.io/easy_rust/Chapter_45.html
}
// The RefCell<T> and Cell<T> type is useful when you’re sure your code follows the borrowing rules but the compiler
// is unable to understand and guarantee that.
// https://dhghomon.github.io/easy_rust/Chapter_41.html
// https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#refcellt-and-the-interior-mutability-pattern
fn cell() {
// Cell let's you mutate inner value without having a mutable reference to the value.
// It's safe because all the getters have a copied version of the inner value and it's limited to
// single thread. Which means that there are no concurrent updates.
let mut a = Cell::new(100);
*(a.get_mut()) = 69;
assert_eq!(a.get(), 69);
// immutable borrow
let b = &a;
// mutable borrow
//let c = &mut a; <-- comptime error as we can't borrow something as mutable when it's already borrowed as immutable
// instead if we need to change the inner value we can call Cell.set
b.set(33);
println!("{}", b.get());
// When to use cell?
// Cell<T> is typically used for more simple types where copying or moving values isn’t too resource intensive
// (e.g. numbers), and should usually be preferred over other cell types when possible. For larger and non-copy types,
// RefCell provides some advantages.
}
fn ref_cell() {
// Box<T> allows immutable or mutable borrows checked at compile time;
// Rc<T> allows only immutable borrows checked at compile time;
// RefCell<T> allows immutable or mutable borrows checked at runtime.
// there can be only `1` mutable borrow(exclusive) or `n` immutable borrows
// this gets checked at run time.
// When to use RefCell?
// when you want to mutate inner value without having mutable reference and you don't want to copy inner value
// when doing get. If costs to get copied value are very small then you can use Cell.
// https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#keeping-track-of-borrows-at-runtime-with-refcellt
// https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#having-multiple-owners-of-mutable-data-by-combining-rct-and-refcellt
// https://doc.rust-lang.org/std/cell/index.html#refcellt
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment