Last active
March 5, 2025 07:22
-
-
Save s4hubhamp/046ea9c7e5f0964ac19c3cf70c5f161f to your computer and use it in GitHub Desktop.
Rust Boxes and Cells
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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