Going through rustlings for the first time

ex(19): smart pointers

Changed files
+277 -19
exercises
19_smart_pointers
solutions
19_smart_pointers
+6 -2
.rustlings-state.txt
··· 1 1 DON'T EDIT THIS FILE! 2 2 3 - box1 3 + threads1 4 4 5 5 intro1 6 6 intro2 ··· 76 76 iterators2 77 77 iterators3 78 78 iterators4 79 - iterators5 79 + iterators5 80 + box1 81 + rc1 82 + arc1 83 + cow1
+2
exercises/19_smart_pointers/arc1.rs
··· 24 24 25 25 // TODO: Define `shared_numbers` by using `Arc`. 26 26 // let shared_numbers = ???; 27 + let shared_numbers = Arc::new(numbers); 27 28 28 29 let mut join_handles = Vec::new(); 29 30 30 31 for offset in 0..8 { 31 32 // TODO: Define `child_numbers` using `shared_numbers`. 32 33 // let child_numbers = ???; 34 + let child_numbers = Arc::clone(&shared_numbers); 33 35 34 36 let handle = thread::spawn(move || { 35 37 let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
+3 -3
exercises/19_smart_pointers/box1.rs
··· 12 12 // TODO: Use a `Box` in the enum definition to make the code compile. 13 13 #[derive(PartialEq, Debug)] 14 14 enum List { 15 - Cons(i32, List), 15 + Cons(i32, Box<List>), 16 16 Nil, 17 17 } 18 18 19 19 // TODO: Create an empty cons list. 20 20 fn create_empty_list() -> List { 21 - todo!() 21 + List::Nil 22 22 } 23 23 24 24 // TODO: Create a non-empty cons list. 25 25 fn create_non_empty_list() -> List { 26 - todo!() 26 + List::Cons(1, Box::new(List::Nil)) 27 27 } 28 28 29 29 fn main() {
+3 -3
exercises/19_smart_pointers/cow1.rs
··· 39 39 let mut input = Cow::from(&vec); 40 40 abs_all(&mut input); 41 41 // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. 42 - assert!(matches!(input, todo!())); 42 + assert!(matches!(input, Cow::Borrowed(_))); 43 43 } 44 44 45 45 #[test] ··· 52 52 let mut input = Cow::from(vec); 53 53 abs_all(&mut input); 54 54 // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. 55 - assert!(matches!(input, todo!())); 55 + assert!(matches!(input, Cow::Owned(_))); 56 56 } 57 57 58 58 #[test] ··· 64 64 let mut input = Cow::from(vec); 65 65 abs_all(&mut input); 66 66 // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. 67 - assert!(matches!(input, todo!())); 67 + assert!(matches!(input, Cow::Owned(_))); 68 68 } 69 69 }
+6 -3
exercises/19_smart_pointers/rc1.rs
··· 60 60 jupiter.details(); 61 61 62 62 // TODO 63 - let saturn = Planet::Saturn(Rc::new(Sun)); 63 + let saturn = Planet::Saturn(Rc::clone(&sun)); 64 64 println!("reference count = {}", Rc::strong_count(&sun)); // 7 references 65 65 saturn.details(); 66 66 67 67 // TODO 68 - let uranus = Planet::Uranus(Rc::new(Sun)); 68 + let uranus = Planet::Uranus(Rc::clone(&sun)); 69 69 println!("reference count = {}", Rc::strong_count(&sun)); // 8 references 70 70 uranus.details(); 71 71 72 72 // TODO 73 - let neptune = Planet::Neptune(Rc::new(Sun)); 73 + let neptune = Planet::Neptune(Rc::clone(&sun)); 74 74 println!("reference count = {}", Rc::strong_count(&sun)); // 9 references 75 75 neptune.details(); 76 76 ··· 92 92 println!("reference count = {}", Rc::strong_count(&sun)); // 4 references 93 93 94 94 // TODO 95 + drop(earth); 95 96 println!("reference count = {}", Rc::strong_count(&sun)); // 3 references 96 97 97 98 // TODO 99 + drop(venus); 98 100 println!("reference count = {}", Rc::strong_count(&sun)); // 2 references 99 101 100 102 // TODO 103 + drop(mercury); 101 104 println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference 102 105 103 106 assert_eq!(Rc::strong_count(&sun), 1);
+43 -2
solutions/19_smart_pointers/arc1.rs
··· 1 + // In this exercise, we are given a `Vec` of `u32` called `numbers` with values 2 + // ranging from 0 to 99. We would like to use this set of numbers within 8 3 + // different threads simultaneously. Each thread is going to get the sum of 4 + // every eighth value with an offset. 5 + // 6 + // The first thread (offset 0), will sum 0, 8, 16, … 7 + // The second thread (offset 1), will sum 1, 9, 17, … 8 + // The third thread (offset 2), will sum 2, 10, 18, … 9 + // … 10 + // The eighth thread (offset 7), will sum 7, 15, 23, … 11 + // 12 + // Each thread should own a reference-counting pointer to the vector of 13 + // numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`. 14 + // 15 + // Don't get distracted by how threads are spawned and joined. We will practice 16 + // that later in the exercises about threads. 17 + 18 + // Don't change the lines below. 19 + #![forbid(unused_imports)] 20 + use std::{sync::Arc, thread}; 21 + 1 22 fn main() { 2 - // DON'T EDIT THIS SOLUTION FILE! 3 - // It will be automatically filled after you finish the exercise. 23 + let numbers: Vec<_> = (0..100u32).collect(); 24 + 25 + let shared_numbers = Arc::new(numbers); 26 + // ^^^^^^^^^^^^^^^^^ 27 + 28 + let mut join_handles = Vec::new(); 29 + 30 + for offset in 0..8 { 31 + let child_numbers = Arc::clone(&shared_numbers); 32 + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 33 + 34 + let handle = thread::spawn(move || { 35 + let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); 36 + println!("Sum of offset {offset} is {sum}"); 37 + }); 38 + 39 + join_handles.push(handle); 40 + } 41 + 42 + for handle in join_handles.into_iter() { 43 + handle.join().unwrap(); 44 + } 4 45 }
+45 -2
solutions/19_smart_pointers/box1.rs
··· 1 + // At compile time, Rust needs to know how much space a type takes up. This 2 + // becomes problematic for recursive types, where a value can have as part of 3 + // itself another value of the same type. To get around the issue, we can use a 4 + // `Box` - a smart pointer used to store data on the heap, which also allows us 5 + // to wrap a recursive type. 6 + // 7 + // The recursive type we're implementing in this exercise is the "cons list", a 8 + // data structure frequently found in functional programming languages. Each 9 + // item in a cons list contains two elements: The value of the current item and 10 + // the next item. The last item is a value called `Nil`. 11 + 12 + #[derive(PartialEq, Debug)] 13 + enum List { 14 + Cons(i32, Box<List>), 15 + Nil, 16 + } 17 + 18 + fn create_empty_list() -> List { 19 + List::Nil 20 + } 21 + 22 + fn create_non_empty_list() -> List { 23 + List::Cons(42, Box::new(List::Nil)) 24 + } 25 + 1 26 fn main() { 2 - // DON'T EDIT THIS SOLUTION FILE! 3 - // It will be automatically filled after you finish the exercise. 27 + println!("This is an empty cons list: {:?}", create_empty_list()); 28 + println!( 29 + "This is a non-empty cons list: {:?}", 30 + create_non_empty_list(), 31 + ); 32 + } 33 + 34 + #[cfg(test)] 35 + mod tests { 36 + use super::*; 37 + 38 + #[test] 39 + fn test_create_empty_list() { 40 + assert_eq!(create_empty_list(), List::Nil); 41 + } 42 + 43 + #[test] 44 + fn test_create_non_empty_list() { 45 + assert_ne!(create_empty_list(), create_non_empty_list()); 46 + } 4 47 }
+67 -2
solutions/19_smart_pointers/cow1.rs
··· 1 + // This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can 2 + // enclose and provide immutable access to borrowed data and clone the data 3 + // lazily when mutation or ownership is required. The type is designed to work 4 + // with general borrowed data via the `Borrow` trait. 5 + 6 + use std::borrow::Cow; 7 + 8 + fn abs_all(input: &mut Cow<[i32]>) { 9 + for ind in 0..input.len() { 10 + let value = input[ind]; 11 + if value < 0 { 12 + // Clones into a vector if not already owned. 13 + input.to_mut()[ind] = -value; 14 + } 15 + } 16 + } 17 + 1 18 fn main() { 2 - // DON'T EDIT THIS SOLUTION FILE! 3 - // It will be automatically filled after you finish the exercise. 19 + // You can optionally experiment here. 20 + } 21 + 22 + #[cfg(test)] 23 + mod tests { 24 + use super::*; 25 + 26 + #[test] 27 + fn reference_mutation() { 28 + // Clone occurs because `input` needs to be mutated. 29 + let vec = vec![-1, 0, 1]; 30 + let mut input = Cow::from(&vec); 31 + abs_all(&mut input); 32 + assert!(matches!(input, Cow::Owned(_))); 33 + } 34 + 35 + #[test] 36 + fn reference_no_mutation() { 37 + // No clone occurs because `input` doesn't need to be mutated. 38 + let vec = vec![0, 1, 2]; 39 + let mut input = Cow::from(&vec); 40 + abs_all(&mut input); 41 + assert!(matches!(input, Cow::Borrowed(_))); 42 + // ^^^^^^^^^^^^^^^^ 43 + } 44 + 45 + #[test] 46 + fn owned_no_mutation() { 47 + // We can also pass `vec` without `&` so `Cow` owns it directly. In this 48 + // case, no mutation occurs (all numbers are already absolute) and thus 49 + // also no clone. But the result is still owned because it was never 50 + // borrowed or mutated. 51 + let vec = vec![0, 1, 2]; 52 + let mut input = Cow::from(vec); 53 + abs_all(&mut input); 54 + assert!(matches!(input, Cow::Owned(_))); 55 + // ^^^^^^^^^^^^^ 56 + } 57 + 58 + #[test] 59 + fn owned_mutation() { 60 + // Of course this is also the case if a mutation does occur (not all 61 + // numbers are absolute). In this case, the call to `to_mut()` in the 62 + // `abs_all` function returns a reference to the same data as before. 63 + let vec = vec![-1, 0, 1]; 64 + let mut input = Cow::from(vec); 65 + abs_all(&mut input); 66 + assert!(matches!(input, Cow::Owned(_))); 67 + // ^^^^^^^^^^^^^ 68 + } 4 69 }
+102 -2
solutions/19_smart_pointers/rc1.rs
··· 1 + // In this exercise, we want to express the concept of multiple owners via the 2 + // `Rc<T>` type. This is a model of our solar system - there is a `Sun` type and 3 + // multiple `Planet`s. The planets take ownership of the sun, indicating that 4 + // they revolve around the sun. 5 + 6 + use std::rc::Rc; 7 + 8 + #[derive(Debug)] 9 + struct Sun; 10 + 11 + #[derive(Debug)] 12 + enum Planet { 13 + Mercury(Rc<Sun>), 14 + Venus(Rc<Sun>), 15 + Earth(Rc<Sun>), 16 + Mars(Rc<Sun>), 17 + Jupiter(Rc<Sun>), 18 + Saturn(Rc<Sun>), 19 + Uranus(Rc<Sun>), 20 + Neptune(Rc<Sun>), 21 + } 22 + 23 + impl Planet { 24 + fn details(&self) { 25 + println!("Hi from {self:?}!"); 26 + } 27 + } 28 + 1 29 fn main() { 2 - // DON'T EDIT THIS SOLUTION FILE! 3 - // It will be automatically filled after you finish the exercise. 30 + // You can optionally experiment here. 31 + } 32 + 33 + #[cfg(test)] 34 + mod tests { 35 + use super::*; 36 + 37 + #[test] 38 + fn rc1() { 39 + let sun = Rc::new(Sun); 40 + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference 41 + 42 + let mercury = Planet::Mercury(Rc::clone(&sun)); 43 + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references 44 + mercury.details(); 45 + 46 + let venus = Planet::Venus(Rc::clone(&sun)); 47 + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references 48 + venus.details(); 49 + 50 + let earth = Planet::Earth(Rc::clone(&sun)); 51 + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references 52 + earth.details(); 53 + 54 + let mars = Planet::Mars(Rc::clone(&sun)); 55 + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references 56 + mars.details(); 57 + 58 + let jupiter = Planet::Jupiter(Rc::clone(&sun)); 59 + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references 60 + jupiter.details(); 61 + 62 + let saturn = Planet::Saturn(Rc::clone(&sun)); 63 + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references 64 + saturn.details(); 65 + 66 + // TODO 67 + let uranus = Planet::Uranus(Rc::clone(&sun)); 68 + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references 69 + uranus.details(); 70 + 71 + // TODO 72 + let neptune = Planet::Neptune(Rc::clone(&sun)); 73 + println!("reference count = {}", Rc::strong_count(&sun)); // 9 references 74 + neptune.details(); 75 + 76 + assert_eq!(Rc::strong_count(&sun), 9); 77 + 78 + drop(neptune); 79 + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references 80 + 81 + drop(uranus); 82 + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references 83 + 84 + drop(saturn); 85 + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references 86 + 87 + drop(jupiter); 88 + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references 89 + 90 + drop(mars); 91 + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references 92 + 93 + drop(earth); 94 + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references 95 + 96 + drop(venus); 97 + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references 98 + 99 + drop(mercury); 100 + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference 101 + 102 + assert_eq!(Rc::strong_count(&sun), 1); 103 + } 4 104 }