Compare commits

..

3 commits

Author SHA1 Message Date
mo8it 663a03a17b cow1 solution 2024-06-29 02:07:56 +02:00
mo8it a943f5ba32 arc1 solution 2024-06-29 01:48:00 +02:00
mo8it f3842aa746 rc1 solution 2024-06-29 01:20:59 +02:00
7 changed files with 293 additions and 90 deletions

View file

@ -1,39 +1,42 @@
// In this exercise, we are given a Vec of u32 called "numbers" with values // In this exercise, we are given a `Vec` of u32 called `numbers` with values
// ranging from 0 to 99 -- [ 0, 1, 2, ..., 98, 99 ] We would like to use this // ranging from 0 to 99. We would like to use this set of numbers within 8
// set of numbers within 8 different threads simultaneously. Each thread is // different threads simultaneously. Each thread is going to get the sum of
// going to get the sum of every eighth value, with an offset. // every eighth value with an offset.
// //
// The first thread (offset 0), will sum 0, 8, 16, ... // The first thread (offset 0), will sum 0, 8, 16,
// The second thread (offset 1), will sum 1, 9, 17, ... // The second thread (offset 1), will sum 1, 9, 17,
// The third thread (offset 2), will sum 2, 10, 18, ... // The third thread (offset 2), will sum 2, 10, 18,
// ... //
// The eighth thread (offset 7), will sum 7, 15, 23, ... // The eighth thread (offset 7), will sum 7, 15, 23,
// //
// Because we are using threads, our values need to be thread-safe. Therefore, // Because we are using threads, our values need to be thread-safe. Therefore,
// we are using Arc. We need to make a change in each of the two TODOs. // we are using `Arc`.
//
// Make this code compile by filling in a value for `shared_numbers` where the
// first TODO comment is, and create an initial binding for `child_numbers`
// where the second TODO comment is. Try not to create any copies of the
// `numbers` Vec!
#![forbid(unused_imports)] // Do not change this, (or the next) line. // Don't change the lines below.
use std::sync::Arc; #![forbid(unused_imports)]
use std::thread; use std::{sync::Arc, thread};
fn main() { fn main() {
let numbers: Vec<_> = (0..100u32).collect(); let numbers: Vec<_> = (0..100u32).collect();
let shared_numbers = // TODO
let mut joinhandles = Vec::new(); // TODO: Define `shared_numbers` by using `Arc`.
// let shared_numbers = ???;
let mut join_handles = Vec::new();
for offset in 0..8 { for offset in 0..8 {
let child_numbers = // TODO // TODO: Define `child_numbers` using `shared_numbers`.
joinhandles.push(thread::spawn(move || { // let child_numbers = ???;
let handle = thread::spawn(move || {
let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
println!("Sum of offset {} is {}", offset, sum); println!("Sum of offset {offset} is {sum}");
})); });
join_handles.push(handle);
} }
for handle in joinhandles.into_iter() {
for handle in join_handles.into_iter() {
handle.join().unwrap(); handle.join().unwrap();
} }
} }

View file

@ -1,24 +1,18 @@
// This exercise explores the Cow, or Clone-On-Write type. Cow is a // This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can
// clone-on-write smart pointer. It can enclose and provide immutable access to // enclose and provide immutable access to borrowed data and clone the data
// borrowed data, and clone the data lazily when mutation or ownership is // lazily when mutation or ownership is required. The type is designed to work
// required. The type is designed to work with general borrowed data via the // with general borrowed data via the `Borrow` trait.
// Borrow trait.
//
// This exercise is meant to show you what to expect when passing data to Cow.
// Fix the unit tests by checking for Cow::Owned(_) and Cow::Borrowed(_) at the
// TODO markers.
use std::borrow::Cow; use std::borrow::Cow;
fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { fn abs_all(input: &mut Cow<[i32]>) {
for i in 0..input.len() { for ind in 0..input.len() {
let v = input[i]; let value = input[ind];
if v < 0 { if value < 0 {
// Clones into a vector if not already owned. // Clones into a vector if not already owned.
input.to_mut()[i] = -v; input.to_mut()[ind] = -value;
} }
} }
input
} }
fn main() { fn main() {
@ -30,47 +24,45 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn reference_mutation() -> Result<(), &'static str> { fn reference_mutation() {
// Clone occurs because `input` needs to be mutated. // Clone occurs because `input` needs to be mutated.
let slice = [-1, 0, 1]; let vec = vec![-1, 0, 1];
let mut input = Cow::from(&slice[..]); let mut input = Cow::from(&vec);
match abs_all(&mut input) { abs_all(&mut input);
Cow::Owned(_) => Ok(()), assert!(matches!(input, Cow::Owned(_)));
_ => Err("Expected owned value"),
}
} }
#[test] #[test]
fn reference_no_mutation() -> Result<(), &'static str> { fn reference_no_mutation() {
// No clone occurs because `input` doesn't need to be mutated. // No clone occurs because `input` doesn't need to be mutated.
let slice = [0, 1, 2]; let vec = vec![0, 1, 2];
let mut input = Cow::from(&slice[..]); let mut input = Cow::from(&vec);
match abs_all(&mut input) { abs_all(&mut input);
// TODO // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
} assert!(matches!(input, todo!()));
} }
#[test] #[test]
fn owned_no_mutation() -> Result<(), &'static str> { fn owned_no_mutation() {
// We can also pass `slice` without `&` so Cow owns it directly. In this // We can also pass `vec` without `&` so `Cow` owns it directly. In this
// case no mutation occurs and thus also no clone, but the result is // case, no mutation occurs and thus also no clone. But the result is
// still owned because it was never borrowed or mutated. // still owned because it was never borrowed or mutated.
let slice = vec![0, 1, 2]; let vec = vec![0, 1, 2];
let mut input = Cow::from(slice); let mut input = Cow::from(vec);
match abs_all(&mut input) { abs_all(&mut input);
// TODO // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
} assert!(matches!(input, todo!()));
} }
#[test] #[test]
fn owned_mutation() -> Result<(), &'static str> { fn owned_mutation() {
// Of course this is also the case if a mutation does occur. In this // Of course this is also the case if a mutation does occur. In this
// case the call to `to_mut()` in the abs_all() function returns a // case, the call to `to_mut()` in the `abs_all` function returns a
// reference to the same data as before. // reference to the same data as before.
let slice = vec![-1, 0, 1]; let vec = vec![-1, 0, 1];
let mut input = Cow::from(slice); let mut input = Cow::from(vec);
match abs_all(&mut input) { abs_all(&mut input);
// TODO // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
} assert!(matches!(input, todo!()));
} }
} }

View file

@ -1,15 +1,12 @@
// In this exercise, we want to express the concept of multiple owners via the // In this exercise, we want to express the concept of multiple owners via the
// Rc<T> type. This is a model of our solar system - there is a Sun type and // `Rc<T>` type. This is a model of our solar system - there is a `Sun` type and
// multiple Planets. The Planets take ownership of the sun, indicating that they // multiple `Planet`s. The planets take ownership of the sun, indicating that
// revolve around the sun. // they revolve around the sun.
//
// Make this code compile by using the proper Rc primitives to express that the
// sun has multiple owners.
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug)] #[derive(Debug)]
struct Sun {} struct Sun;
#[derive(Debug)] #[derive(Debug)]
enum Planet { enum Planet {
@ -25,7 +22,7 @@ enum Planet {
impl Planet { impl Planet {
fn details(&self) { fn details(&self) {
println!("Hi from {:?}!", self) println!("Hi from {self:?}!");
} }
} }
@ -39,7 +36,7 @@ mod tests {
#[test] #[test]
fn rc1() { fn rc1() {
let sun = Rc::new(Sun {}); let sun = Rc::new(Sun);
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
let mercury = Planet::Mercury(Rc::clone(&sun)); let mercury = Planet::Mercury(Rc::clone(&sun));
@ -63,17 +60,17 @@ mod tests {
jupiter.details(); jupiter.details();
// TODO // TODO
let saturn = Planet::Saturn(Rc::new(Sun {})); let saturn = Planet::Saturn(Rc::new(Sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
saturn.details(); saturn.details();
// TODO // TODO
let uranus = Planet::Uranus(Rc::new(Sun {})); let uranus = Planet::Uranus(Rc::new(Sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
uranus.details(); uranus.details();
// TODO // TODO
let neptune = Planet::Neptune(Rc::new(Sun {})); let neptune = Planet::Neptune(Rc::new(Sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
neptune.details(); neptune.details();

View file

@ -993,18 +993,18 @@ of the `Sun`.
After using `drop()` to move the `Planet`s out of scope individually, the After using `drop()` to move the `Planet`s out of scope individually, the
reference count goes down. reference count goes down.
In the end the `Sun` only has one reference again, to itself. In the end, the `Sun` only has one reference again, to itself.
See more at: https://doc.rust-lang.org/book/ch15-04-rc.html See more at: https://doc.rust-lang.org/book/ch15-04-rc.html
* Unfortunately Pluto is no longer considered a planet :(""" Unfortunately, Pluto is no longer considered a planet :("""
[[exercises]] [[exercises]]
name = "arc1" name = "arc1"
dir = "19_smart_pointers" dir = "19_smart_pointers"
test = false test = false
hint = """ hint = """
Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order Make `shared_numbers` be an `Arc` from the `numbers` vector. Then, in order
to avoid creating a copy of `numbers`, you'll need to create `child_numbers` to avoid creating a copy of `numbers`, you'll need to create `child_numbers`
inside the loop but still in the main thread. inside the loop but still in the main thread.
@ -1020,11 +1020,11 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html"""
name = "cow1" name = "cow1"
dir = "19_smart_pointers" dir = "19_smart_pointers"
hint = """ hint = """
If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is If `Cow` already owns the data, it doesn't need to clone it when `to_mut()` is
called. called.
Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation Check out the documentation of the `Cow` type:
on the `Cow` type.""" https://doc.rust-lang.org/std/borrow/enum.Cow.html"""
# THREADS # THREADS

View file

@ -1 +1,42 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 // In this exercise, we are given a `Vec` of u32 called `numbers` with values
// ranging from 0 to 99. We would like to use this set of numbers within 8
// different threads simultaneously. Each thread is going to get the sum of
// every eighth value with an offset.
//
// The first thread (offset 0), will sum 0, 8, 16, …
// The second thread (offset 1), will sum 1, 9, 17, …
// The third thread (offset 2), will sum 2, 10, 18, …
// …
// The eighth thread (offset 7), will sum 7, 15, 23, …
//
// Because we are using threads, our values need to be thread-safe. Therefore,
// we are using `Arc`.
// Don't change the lines below.
#![forbid(unused_imports)]
use std::{sync::Arc, thread};
fn main() {
let numbers: Vec<_> = (0..100u32).collect();
let shared_numbers = Arc::new(numbers);
// ^^^^^^^^^^^^^^^^^
let mut join_handles = Vec::new();
for offset in 0..8 {
let child_numbers = Arc::clone(&shared_numbers);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
let handle = thread::spawn(move || {
let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
println!("Sum of offset {offset} is {sum}");
});
join_handles.push(handle);
}
for handle in join_handles.into_iter() {
handle.join().unwrap();
}
}

View file

@ -1 +1,68 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 // This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can
// enclose and provide immutable access to borrowed data and clone the data
// lazily when mutation or ownership is required. The type is designed to work
// with general borrowed data via the `Borrow` trait.
use std::borrow::Cow;
fn abs_all(input: &mut Cow<[i32]>) {
for ind in 0..input.len() {
let value = input[ind];
if value < 0 {
// Clones into a vector if not already owned.
input.to_mut()[ind] = -value;
}
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reference_mutation() {
// Clone occurs because `input` needs to be mutated.
let vec = vec![-1, 0, 1];
let mut input = Cow::from(&vec);
abs_all(&mut input);
assert!(matches!(input, Cow::Owned(_)));
}
#[test]
fn reference_no_mutation() {
// No clone occurs because `input` doesn't need to be mutated.
let vec = vec![0, 1, 2];
let mut input = Cow::from(&vec);
abs_all(&mut input);
assert!(matches!(input, Cow::Borrowed(_)));
// ^^^^^^^^^^^^^^^^
}
#[test]
fn owned_no_mutation() {
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
// case, no mutation occurs and thus also no clone. But the result is
// still owned because it was never borrowed or mutated.
let vec = vec![0, 1, 2];
let mut input = Cow::from(vec);
abs_all(&mut input);
assert!(matches!(input, Cow::Owned(_)));
// ^^^^^^^^^^^^^
}
#[test]
fn owned_mutation() {
// Of course this is also the case if a mutation does occur. In this
// case, the call to `to_mut()` in the `abs_all` function returns a
// reference to the same data as before.
let vec = vec![-1, 0, 1];
let mut input = Cow::from(vec);
abs_all(&mut input);
assert!(matches!(input, Cow::Owned(_)));
// ^^^^^^^^^^^^^
}
}

View file

@ -1 +1,104 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 // In this exercise, we want to express the concept of multiple owners via the
// `Rc<T>` type. This is a model of our solar system - there is a `Sun` type and
// multiple `Planet`s. The planets take ownership of the sun, indicating that
// they revolve around the sun.
use std::rc::Rc;
#[derive(Debug)]
struct Sun;
#[derive(Debug)]
enum Planet {
Mercury(Rc<Sun>),
Venus(Rc<Sun>),
Earth(Rc<Sun>),
Mars(Rc<Sun>),
Jupiter(Rc<Sun>),
Saturn(Rc<Sun>),
Uranus(Rc<Sun>),
Neptune(Rc<Sun>),
}
impl Planet {
fn details(&self) {
println!("Hi from {self:?}!");
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rc1() {
let sun = Rc::new(Sun);
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
let mercury = Planet::Mercury(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
mercury.details();
let venus = Planet::Venus(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
venus.details();
let earth = Planet::Earth(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
earth.details();
let mars = Planet::Mars(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 5 references
mars.details();
let jupiter = Planet::Jupiter(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 6 references
jupiter.details();
let saturn = Planet::Saturn(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
saturn.details();
// TODO
let uranus = Planet::Uranus(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
uranus.details();
// TODO
let neptune = Planet::Neptune(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
neptune.details();
assert_eq!(Rc::strong_count(&sun), 9);
drop(neptune);
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
drop(uranus);
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
drop(saturn);
println!("reference count = {}", Rc::strong_count(&sun)); // 6 references
drop(jupiter);
println!("reference count = {}", Rc::strong_count(&sun)); // 5 references
drop(mars);
println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
drop(earth);
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
drop(venus);
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
drop(mercury);
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
assert_eq!(Rc::strong_count(&sun), 1);
}
}