├── .gitattributes └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Rust Ownership Basics : Immutable Reference, mutable Reference, Rc and Refcell 4 | 5 | tldr 6 | 7 | ``` 8 | Avoid `clone()` options ^_^ 9 | | 10 | --------------------------------------------- 11 | |         | 12 | Read-only access?                 Need to mutate? 13 | |                  | 14 | --------------------             ------------------- 15 | | |             | | 16 | No Ownership? Shared Ownership? Exclusive mutability? Shared mutability? 17 | | |             | | 18 |   Use &T     Use Rc         Use &mut     Use Rc> 19 | ``` 20 | 21 | Rust Ownership rules: 22 | 23 | - Each value in Rust has an *owner*. 24 | - There can only be one owner at a time. 25 | - When the owner goes out of scope, the value will be dropped.[1] 26 | 27 | In this post, we will discuss heap-allocated variables and when to use references (&), mutable references (&mut), Rc, or RefCell. 28 | 29 | Simple basics from the Book: 30 | 31 | To ensure memory safety after we assign s1 to s2 (shallow copy), Rust considers `s1` as no longer valid - s1 goes out of scope , ownership is transfered to s2. Therefore, it is impossible to print s1 afterward. 32 | 33 | ```rust 34 | fn main() { 35 | let s1 = String::from("hello"); 36 | let s2 = s1; // ownership moved to s2 37 | println!("{s1} {s2}"); // s1 is no longer valid 38 | } 39 | ``` 40 | 41 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=14dca9f937e10df439d964c64d983857) 42 | 43 | In this case, we might be tempted to use clone() (deep copy of heap data) to satisfy the compiler: 44 | 45 | ```rust 46 | fn main() { 47 | let s1 = String::from("hello"); 48 | let s2 = s1.clone(); // deep copy of s1 49 | println!("{s1},{s2}!"); // we can print s1 and s2 50 | } 51 | ``` 52 | 53 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=14dca9f937e10df439d964c64d983857) 54 | 55 | But copying heap data is an expensive operation and to achieve the same result for this read-only functionality we have nore efficient option without transfering ownership or deep copy : an immutable refference. 56 | 57 | 58 | ### Immutable refference (&T) 59 | 60 | 61 | ```rust 62 | fn main() { 63 | let s1 = String::from("hello"); 64 | let s2 = &s1; // borrowing s1 ie creating reference 65 | println!("{s1},{s2}!"); // we can print s1 and s2 66 | } 67 | ``` 68 | 69 | [playgroung](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4bc09ce6a93535fa7bda99d56ed926cb) 70 | 71 | So we can use an immutable refference when we do not need to take ownership of variable and we do not need to mutate it. 72 | 73 | We can have multiple immutable references to a variable, but these references are only valid for as long as the variable itself remains in scope. Once the variable goes out of scope, all its immutable references become invalid. 74 | 75 | But what if different parts of our code need to access the variable simultaneously? 76 | 77 | ```rust 78 | fn main() { 79 | let s2; 80 | { 81 | let s1 = String::from("hello"); 82 | s2 = &s1; // borrowing s1, i.e., creating a reference 83 | } // s1 goes out of scope, so the borrowed value is no longer valid 84 | println!("{s2}"); // Error: cannot print s2 because the borrowed value does not live long enough 85 | } 86 | ``` 87 | 88 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=71a9c5af25405522e9300420e4e247a4) 89 | 90 | If we will try to compile this code we will get error:`borrowed value does not live long enough`. 91 | 92 | This is where smart pointers like Rc can help. 93 | 94 | 95 | ### Refference Count (Rc) (single-threaded) 96 | 97 | 98 | [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html "struct std::rc::Rc") stands for Reference Count. It provides shared ownership of a value of type `T`, allocated on the heap. It allows multiple parts of a program to have ownership of the same data, and the value will only be dropped when the last reference goes out of scope. 99 | 100 | Invoking [`clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html#tymethod.clone "method std::clone::Clone::clone") on Rc produces a new pointer to the same allocation in the heap.[2] 101 | 102 | ```rust 103 | use std::rc::Rc; 104 | 105 | fn main() { 106 | let s2; 107 | { 108 | let s1 = Rc::new(String::from("hello")); // Create a reference-counted string. 109 | s2 = Rc::clone(&s1); //Cloning the s1 produces a new pointer to the same allocation in the heap 110 | } // s1 goes out of scope, but the String it points to remains valid because s2 still references it 111 | println!("{s2}"); // we can print s2 112 | } 113 | ``` 114 | 115 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e14c17e43e7be4077460eb0b474e9cd6) 116 | 117 | Unlike an immutable reference, Rc allows multiple owners of the same value. So even though `s1` goes out of scope, the value it points to remains valid because `s2` is still referencing it. The reference count ensures the value will only be deallocated when the last owner (`s2` in this case) is dropped. 118 | 119 | Similar to an immutable reference (`&T`), `Rc` allows multiple references to data on the heap, **without allowing mutation by default**. 120 | 121 | ```rust 122 | use std::rc::Rc; 123 | 124 | fn main() { 125 | let s1 = Rc::new(String::from("hello")); 126 | let s2 = Rc::clone(&s1); 127 | let s3 = Rc::clone(&s1); 128 | println!("{s1} {s2} {s3}"); 129 | // s2 and s3 both point to the same memory location as s1. 130 | } 131 | ``` 132 | 133 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=544f474c04a5a61490f057d89afb6dc3) 134 | 135 | These are some primitive examples that simply help illustrate the logic. For more advanced examples and use cases, you can refer to the official documentation [std::rc - Rust](https://doc.rust-lang.org/std/rc/index.html) 136 | 137 | As we mentioned above, immutable reference and Rc are both not allowing mutation as default, but what if we need to mutate data on the heap? 138 | 139 | 140 | ### Mutable reference (&mut T) 141 | 142 | 143 | When we need to mutate a borrowed value, the most obvious option is to use mutable reference (& mut). 144 | 145 | ```rust 146 | fn main() { 147 | let mut s1 = String::from("hello"); 148 | change(&mut s1); 149 | println!("{s1}"); 150 | } 151 | 152 | fn change(some_string: &mut String) { 153 | some_string.push_str(", world"); 154 | } 155 | ``` 156 | 157 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7d9ee65cbd7a9dded369c581fdc8e9dc) 158 | 159 | Mutable references have one big restriction: if you have a mutable reference to 160 | a value, you can have no other simultaneous references to that value. 161 | 162 | It is not allowed to have immutable refference if there is already mutable reference exists. 163 | 164 | ```rust 165 | fn main() { 166 | let mut s1 = String::from("hello"); 167 | let s2 = &mut s1; // mutable borrowing s1 168 | let s3 = &s1; // cannot borrow `s1` as immutable because it is also borrowed as mutable 169 | println!("{s1},{s2},{s3}!"); 170 | } 171 | ``` 172 | 173 | It's also not allowed to have two simultaneous mutable references to the same value: 174 | 175 | ```rust 176 | fn main() { 177 | let mut s1 = String::from("hello"); 178 | let s2 = &mut s1; // mutable borrowing s1 179 | let s3 = &mut s1; // cannot borrow `s1` as mutable more than once at a time 180 | println!("{s1},{s2},{s3}!"); 181 | } 182 | ``` 183 | 184 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8c2f4c0d3b795a3a6bbc0620d86fff3d) 185 | 186 | However, Rust's borrowing rules don’t allow more than one mutable reference at a time, which can be limiting in certain scenarios. For example, what if you need multiple parts of your program to mutate the same data simultaneously, without violating Rust’s safety? This is where the combination of `Rc` and `RefCell` comes into play, allowing shared ownership *and* mutation. 187 | 188 | 189 | ### Combining RefCell and Rc we can have multiple owners of mutable data. (single-threaded) 190 | 191 | 192 | A common way to use `RefCell` is in combination with `Rc`. Recall that `Rc` lets you have multiple owners of some data, but it only gives immutable access to that data by default. If you have an `Rc` that holds a `RefCell`, you can get a value that can have multiple owners *and* that you can mutate! [3] 193 | 194 | ```rust 195 | use std::rc::Rc; 196 | use std::cell::RefCell; 197 | 198 | fn main() { 199 | let s1 = Rc::new(RefCell::new(String::from("hello"))); 200 | 201 | let s2 = Rc::clone(&s1); 202 | let s3 = Rc::clone(&s1); 203 | 204 | // Now both s2 and s3 can mutate the shared string 205 | s2.borrow_mut().push_str(", world"); 206 | s3.borrow_mut().push_str("!"); 207 | 208 | println!("{:?}", s1); // hello, world! 209 | } 210 | ``` 211 | 212 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f523b9c75de6c9528e672ea75a9cc276) 213 | 214 | But of course true power of combining Rc and RefCell comes with complex data structures such as linked lists, trees etc. 215 | 216 | Binary tree example [4]: 217 | 218 | ```rust 219 | use std::cell::RefCell; 220 | use std::fmt::Debug; 221 | use std::rc::Rc; 222 | 223 | #[derive(Debug, PartialEq, Eq, Clone)] 224 | pub struct TreeNode { 225 | pub val: T, 226 | pub left: Option>>>, 227 | pub right: Option>>>, 228 | } 229 | impl TreeNode { 230 | pub fn new(val: T) -> Self { 231 | TreeNode { 232 | val, 233 | left: None, 234 | right: None, 235 | } 236 | } 237 | } 238 | 239 | pub fn preorder_traversal(root: &Option>>>) { 240 | match root { 241 | None => {} 242 | Some(node) => { 243 | let borrowed_node = node.borrow(); 244 | println!("{:?}", borrowed_node.val); 245 | preorder_traversal(&borrowed_node.left.clone()); 246 | preorder_traversal(&borrowed_node.right.clone()); 247 | } 248 | } 249 | } 250 | 251 | pub fn inorder_traversal(root: &Option>>>) { 252 | match root { 253 | None => {} 254 | Some(node) => { 255 | let borrowed_node = node.borrow(); 256 | inorder_traversal(&borrowed_node.left.clone()); 257 | println!("{:?}", borrowed_node.val); 258 | inorder_traversal(&borrowed_node.right.clone()); 259 | } 260 | } 261 | } 262 | 263 | pub fn postorder_traversal(root: &Option>>>) { 264 | if let Some(node) = root { 265 | let borrowed_node = node.borrow(); 266 | postorder_traversal(&borrowed_node.left.clone()); 267 | postorder_traversal(&borrowed_node.right.clone()); 268 | println!("{:?}", borrowed_node.val); 269 | } 270 | } 271 | 272 | pub fn calculate_max_depth(root: &Option>>>) -> i32 { 273 | match root { 274 | None => return 0, 275 | Some(node) => { 276 | let borrowed_node = node.borrow(); 277 | let left_depth = calculate_max_depth(&borrowed_node.left.clone()); 278 | let right_depth = calculate_max_depth(&borrowed_node.right.clone()); 279 | if left_depth > right_depth { 280 | return left_depth + 1; 281 | } else { 282 | return right_depth + 1; 283 | } 284 | } 285 | } 286 | } 287 | 288 | /* 289 | 1 290 | / \ 291 | 2 3 292 | / \ / \ 293 | 4 5 6 7 294 | 295 | */ 296 | 297 | fn main() { 298 | let mut root = TreeNode::new(1); 299 | root.left = Some(Rc::new(RefCell::new(TreeNode::new(2)))); 300 | root.right = Some(Rc::new(RefCell::new(TreeNode::new(3)))); 301 | if let Some(left_node) = root.left.as_mut() { 302 | //same as with root we should make root.left mut to modify 303 | left_node.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(4)))); 304 | // borrowing is obtain mut ref to Refcall containing root.left, 305 | // ie to access and mutate the left field of the root inside the RefCell contained within the Rc. 306 | } 307 | if let Some(right_node) = root.left.as_mut() { 308 | right_node.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(5)))); 309 | } 310 | 311 | if let Some(left_node) = root.right.as_mut() { 312 | left_node.borrow_mut().left = Some(Rc::new(RefCell::new(TreeNode::new(6)))); 313 | } 314 | if let Some(right_node) = root.right.as_mut() { 315 | right_node.borrow_mut().right = Some(Rc::new(RefCell::new(TreeNode::new(7)))); 316 | } 317 | 318 | println!("{:?}", root); 319 | println!("Preorder Traversal:"); 320 | preorder_traversal(&Some(Rc::new(RefCell::new(root.clone())))); 321 | println!("Inorder Traversal:"); 322 | inorder_traversal(&Some(Rc::new(RefCell::new(root.clone())))); 323 | println!("Postorder Traversal:"); 324 | postorder_traversal(&Some(Rc::new(RefCell::new(root.clone())))); 325 | println!( 326 | "Max depth is: {:?}", 327 | calculate_max_depth(&Some(Rc::new(RefCell::new(root)))) 328 | ); 329 | } 330 | ``` 331 | 332 | [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=6819e1f13f8f26f4c08dd5cdca27cc01) 333 | 334 | 335 | **Summary:** 336 | 337 | - Use &T for non-owning, read-only access. 338 | - Use &mut T for non-owning, exclusive mutable access. 339 | - Use Rc for shared ownership when you don't need mutability. 340 | - Combine Rc with RefCell for shared ownership with mutability. 341 | 342 | 343 | Source: 344 | 345 | [1] [What is Ownership? - The Rust Programming Language](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html) 346 | 347 | [2][ std::rc - Rust](https://doc.rust-lang.org/std/rc/index.html) 348 | 349 | [3][RefCell<T> and the Interior Mutability Pattern - The Rust Programming Language](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) 350 | 351 | [4][algos_data_structures_rust/src/trees/binary_trees/binary_tree.rs at master · tracyspacy/algos_data_structures_rust · GitHub](https://github.com/tracyspacy/algos_data_structures_rust/blob/master/src/trees/binary_trees/binary_tree.rs) 352 | --------------------------------------------------------------------------------