From 97dffa67c86ed01d7be1b7e0ce6539a187f3ddb0 Mon Sep 17 00:00:00 2001 From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:26:25 +0200 Subject: [PATCH 1/4] Solutions --- Cargo.lock | 11 +++ Cargo.toml | 3 + exercises/01_intro/00_welcome/src/lib.rs | 2 +- exercises/01_intro/01_syntax/src/lib.rs | 4 +- .../02_basic_calculator/00_intro/src/lib.rs | 2 +- .../01_integers/src/lib.rs | 5 +- .../02_variables/src/lib.rs | 1 + .../02_basic_calculator/03_if_else/src/lib.rs | 8 ++- .../02_basic_calculator/04_panics/src/lib.rs | 3 + .../05_factorial/src/lib.rs | 7 ++ .../02_basic_calculator/06_while/src/lib.rs | 8 ++- .../02_basic_calculator/07_for/src/lib.rs | 6 +- .../09_saturating/src/lib.rs | 4 +- .../10_as_casting/src/lib.rs | 8 +-- exercises/03_ticket_v1/00_intro/src/lib.rs | 2 +- exercises/03_ticket_v1/01_struct/src/lib.rs | 10 +++ .../03_ticket_v1/02_validation/src/lib.rs | 19 ++++- exercises/03_ticket_v1/03_modules/src/lib.rs | 1 + .../03_ticket_v1/04_visibility/src/lib.rs | 16 ++--- .../03_ticket_v1/05_encapsulation/src/lib.rs | 11 +++ .../03_ticket_v1/06_ownership/src/lib.rs | 12 ++-- exercises/03_ticket_v1/07_setters/src/lib.rs | 57 +++++++++++---- exercises/03_ticket_v1/08_stack/src/lib.rs | 6 +- exercises/03_ticket_v1/09_heap/src/lib.rs | 8 +-- .../10_references_in_memory/src/lib.rs | 9 ++- .../03_ticket_v1/11_destructor/src/lib.rs | 2 +- exercises/03_ticket_v1/12_outro/src/lib.rs | 71 ++++++++++++++++++ exercises/04_traits/00_intro/src/lib.rs | 2 +- exercises/04_traits/01_trait/src/lib.rs | 15 ++++ exercises/04_traits/02_orphan_rule/src/lib.rs | 6 -- .../03_operator_overloading/src/lib.rs | 8 ++- exercises/04_traits/04_derive/src/lib.rs | 2 +- .../04_traits/05_trait_bounds/src/lib.rs | 5 +- exercises/04_traits/06_str_slice/src/lib.rs | 6 +- exercises/04_traits/07_deref/src/lib.rs | 4 +- exercises/04_traits/08_sized/src/lib.rs | 2 +- exercises/04_traits/09_from/src/lib.rs | 6 ++ .../04_traits/10_assoc_vs_generic/src/lib.rs | 30 ++++++++ exercises/04_traits/11_clone/src/lib.rs | 3 +- exercises/04_traits/12_copy/src/lib.rs | 9 +++ exercises/04_traits/13_drop/src/lib.rs | 21 ++++++ exercises/04_traits/14_outro/src/lib.rs | 72 +++++++++++++++++++ exercises/05_ticket_v2/00_intro/src/lib.rs | 2 +- exercises/05_ticket_v2/01_enum/src/lib.rs | 14 ++-- exercises/05_ticket_v2/02_match/src/lib.rs | 7 +- .../03_variants_with_data/src/lib.rs | 5 +- exercises/05_ticket_v2/04_if_let/src/lib.rs | 6 +- .../05_ticket_v2/05_nullability/src/lib.rs | 5 +- .../05_ticket_v2/06_fallibility/src/lib.rs | 14 ++-- exercises/05_ticket_v2/07_unwrap/src/lib.rs | 11 ++- .../05_ticket_v2/08_error_enums/src/lib.rs | 30 ++++++-- .../05_ticket_v2/09_error_trait/src/lib.rs | 22 +++++- exercises/05_ticket_v2/10_packages/src/lib.rs | 3 + .../05_ticket_v2/11_dependencies/Cargo.toml | 3 + .../05_ticket_v2/12_thiserror/Cargo.toml | 1 + .../05_ticket_v2/12_thiserror/src/lib.rs | 5 ++ exercises/05_ticket_v2/13_try_from/Cargo.toml | 1 + exercises/05_ticket_v2/13_try_from/src/lib.rs | 29 ++++++++ exercises/05_ticket_v2/14_source/src/lib.rs | 5 +- exercises/05_ticket_v2/15_outro/Cargo.toml | 3 + .../05_ticket_v2/15_outro/src/description.rs | 38 +++++++++- exercises/05_ticket_v2/15_outro/src/status.rs | 31 ++++++++ exercises/05_ticket_v2/15_outro/src/title.rs | 37 ++++++++++ .../06_ticket_management/00_intro/src/lib.rs | 2 +- .../06_ticket_management/01_arrays/src/lib.rs | 24 +++++-- .../06_ticket_management/02_vec/src/lib.rs | 11 +-- .../03_resizing/src/lib.rs | 2 +- .../04_iterators/src/lib.rs | 9 +++ .../06_ticket_management/05_iter/src/lib.rs | 4 ++ .../06_lifetimes/src/lib.rs | 9 +++ .../07_combinators/src/lib.rs | 7 ++ .../08_impl_trait/src/lib.rs | 6 ++ .../09_impl_trait_2/src/lib.rs | 2 +- .../06_ticket_management/10_slices/src/lib.rs | 4 ++ .../11_mutable_slices/src/lib.rs | 6 ++ .../12_two_states/src/lib.rs | 17 ++++- .../06_ticket_management/13_index/src/lib.rs | 17 +++++ .../14_index_mut/src/lib.rs | 14 +++- .../15_hashmap/src/lib.rs | 10 +-- .../16_btreemap/src/lib.rs | 19 +++-- exercises/07_threads/00_intro/src/lib.rs | 2 +- exercises/07_threads/01_threads/src/lib.rs | 10 ++- exercises/07_threads/02_static/src/lib.rs | 8 ++- exercises/07_threads/03_leak/src/lib.rs | 9 ++- .../07_threads/04_scoped_threads/src/lib.rs | 9 ++- exercises/07_threads/05_channels/src/lib.rs | 15 +++- .../07_threads/05_channels/tests/insert.rs | 2 +- .../06_interior_mutability/src/lib.rs | 6 +- exercises/07_threads/07_ack/src/lib.rs | 31 +++++--- exercises/07_threads/08_client/src/lib.rs | 24 +++++-- exercises/07_threads/09_bounded/Cargo.toml | 1 + exercises/07_threads/09_bounded/src/lib.rs | 44 ++++++++---- exercises/07_threads/10_patch/src/lib.rs | 24 ++++++- exercises/07_threads/11_locks/src/store.rs | 6 +- exercises/07_threads/12_rw_lock/src/lib.rs | 8 +-- exercises/07_threads/12_rw_lock/src/store.rs | 8 +-- .../13_without_channels/tests/check.rs | 2 +- exercises/07_threads/14_sync/src/lib.rs | 2 +- exercises/08_futures/00_intro/src/lib.rs | 2 +- exercises/08_futures/01_async_fn/src/lib.rs | 6 +- exercises/08_futures/02_spawn/src/lib.rs | 17 ++++- exercises/08_futures/03_runtime/src/lib.rs | 21 +++++- exercises/08_futures/04_future/src/lib.rs | 6 +- exercises/08_futures/05_blocking/src/lib.rs | 12 ++-- .../06_async_aware_primitives/src/lib.rs | 16 +++-- .../08_futures/07_cancellation/src/lib.rs | 2 +- 106 files changed, 1018 insertions(+), 197 deletions(-) create mode 100644 exercises/05_ticket_v2/10_packages/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d560369829..617822c206 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,6 +205,7 @@ dependencies = [ name = "bounded" version = "0.1.0" dependencies = [ + "thiserror", "ticket_fields", ] @@ -443,6 +444,9 @@ dependencies = [ [[package]] name = "deps" version = "0.1.0" +dependencies = [ + "anyhow", +] [[package]] name = "deref" @@ -1439,6 +1443,9 @@ version = "0.1.0" [[package]] name = "outro_04" version = "0.1.0" +dependencies = [ + "thiserror", +] [[package]] name = "outro_08" @@ -2175,6 +2182,7 @@ name = "thiserror_" version = "0.1.0" dependencies = [ "common", + "thiserror", ] [[package]] @@ -2316,6 +2324,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tryfrom" version = "0.1.0" +dependencies = [ + "thiserror", +] [[package]] name = "tungstenite" diff --git a/Cargo.toml b/Cargo.toml index 52e8a87aa4..f53da2ac81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ resolver = "2" # regardless of the "global" setting for `overflow-checks` on the `dev` profile. [profile.dev.package.copy] overflow-checks = true + +[profile.dev] +overflow-checks = false diff --git a/exercises/01_intro/00_welcome/src/lib.rs b/exercises/01_intro/00_welcome/src/lib.rs index ec95b6ca75..e0f1fc2653 100644 --- a/exercises/01_intro/00_welcome/src/lib.rs +++ b/exercises/01_intro/00_welcome/src/lib.rs @@ -17,7 +17,7 @@ // You can also find solutions to all exercises in the `solutions` git branch. fn greeting() -> &'static str { // TODO: fix me πŸ‘‡ - "I'm ready to __!" + "I'm ready to learn Rust!" } // Your solutions will be automatically verified by a set of tests. diff --git a/exercises/01_intro/01_syntax/src/lib.rs b/exercises/01_intro/01_syntax/src/lib.rs index 676d81b22e..1845605a53 100644 --- a/exercises/01_intro/01_syntax/src/lib.rs +++ b/exercises/01_intro/01_syntax/src/lib.rs @@ -3,7 +3,7 @@ // partner in this course and it'll often guide you in the right direction! // // The input parameters should have the same type of the return type. -fn compute(a, b) -> u32 { +fn compute(a: u32, b: u32) -> u32 { // Don't touch the function body. a + b * 2 } @@ -16,4 +16,4 @@ mod tests { fn case() { assert_eq!(compute(1, 2), 5); } -} \ No newline at end of file +} diff --git a/exercises/02_basic_calculator/00_intro/src/lib.rs b/exercises/02_basic_calculator/00_intro/src/lib.rs index 03aeb16339..d65a5cd207 100644 --- a/exercises/02_basic_calculator/00_intro/src/lib.rs +++ b/exercises/02_basic_calculator/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me πŸ‘‡ - "I'm ready to __!" + "I'm ready to build a calculator in Rust!" } #[cfg(test)] diff --git a/exercises/02_basic_calculator/01_integers/src/lib.rs b/exercises/02_basic_calculator/01_integers/src/lib.rs index a87b56fba3..7801db5fc5 100644 --- a/exercises/02_basic_calculator/01_integers/src/lib.rs +++ b/exercises/02_basic_calculator/01_integers/src/lib.rs @@ -1,6 +1,7 @@ fn compute(a: u32, b: u32) -> u32 { - // TODO: change the line below to fix the compiler error and make the tests pass. - let multiplier: u8 = 4; + let multiplier: u32 = 4; + // ^ You could also omit `: u32` entirely. + // The compiler will infer the correct type based on the usage below. a + b * multiplier } diff --git a/exercises/02_basic_calculator/02_variables/src/lib.rs b/exercises/02_basic_calculator/02_variables/src/lib.rs index e8d116749a..e5a63b2ae6 100644 --- a/exercises/02_basic_calculator/02_variables/src/lib.rs +++ b/exercises/02_basic_calculator/02_variables/src/lib.rs @@ -8,6 +8,7 @@ pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 { // TODO: define a variable named `distance` with the right value to get tests to pass // Do you need to annotate the type of `distance`? Why or why not? + let distance = end - start; // Don't change the line below distance / time_elapsed diff --git a/exercises/02_basic_calculator/03_if_else/src/lib.rs b/exercises/02_basic_calculator/03_if_else/src/lib.rs index fa2e06ea76..5c396e50b3 100644 --- a/exercises/02_basic_calculator/03_if_else/src/lib.rs +++ b/exercises/02_basic_calculator/03_if_else/src/lib.rs @@ -2,7 +2,13 @@ /// `13` if `n` is divisible by `3`, /// `17` otherwise. fn magic_number(n: u32) -> u32 { - todo!() + if n % 2 == 0 { + 12 + } else if n % 3 == 0 { + 13 + } else { + 17 + } } #[cfg(test)] diff --git a/exercises/02_basic_calculator/04_panics/src/lib.rs b/exercises/02_basic_calculator/04_panics/src/lib.rs index 702b7bd85c..f70452fa18 100644 --- a/exercises/02_basic_calculator/04_panics/src/lib.rs +++ b/exercises/02_basic_calculator/04_panics/src/lib.rs @@ -2,6 +2,9 @@ /// calculate the average speed of the journey. fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 { // TODO: Panic with a custom message if `time_elapsed` is 0 + if time_elapsed == 0 { + panic!("The journey took no time at all, that's impossible!"); + } (end - start) / time_elapsed } diff --git a/exercises/02_basic_calculator/05_factorial/src/lib.rs b/exercises/02_basic_calculator/05_factorial/src/lib.rs index d2f11a7216..ef43562aac 100644 --- a/exercises/02_basic_calculator/05_factorial/src/lib.rs +++ b/exercises/02_basic_calculator/05_factorial/src/lib.rs @@ -9,6 +9,13 @@ // `factorial(2)` to return `2`, and so on. // // Use only what you learned! No loops yet, so you'll have to use recursion! +fn factorial(n: u32) -> u32 { + if n == 0 { + 1 + } else { + n * factorial(n - 1) + } +} #[cfg(test)] mod tests { diff --git a/exercises/02_basic_calculator/06_while/src/lib.rs b/exercises/02_basic_calculator/06_while/src/lib.rs index dbc30ebb2d..a5f3d69c4d 100644 --- a/exercises/02_basic_calculator/06_while/src/lib.rs +++ b/exercises/02_basic_calculator/06_while/src/lib.rs @@ -4,7 +4,13 @@ pub fn factorial(n: u32) -> u32 { // interprets as "I'll get back to this later", thus // suppressing type errors. // It panics at runtime. - todo!() + let mut result = 1; + let mut i = 1; + while i <= n { + result *= i; + i += 1; + } + result } #[cfg(test)] diff --git a/exercises/02_basic_calculator/07_for/src/lib.rs b/exercises/02_basic_calculator/07_for/src/lib.rs index d571d57d90..6ba14f489d 100644 --- a/exercises/02_basic_calculator/07_for/src/lib.rs +++ b/exercises/02_basic_calculator/07_for/src/lib.rs @@ -1,6 +1,10 @@ // Rewrite the factorial function using a `for` loop. pub fn factorial(n: u32) -> u32 { - todo!() + let mut result = 1; + for i in 1..=n { + result *= i; + } + result } #[cfg(test)] diff --git a/exercises/02_basic_calculator/09_saturating/src/lib.rs b/exercises/02_basic_calculator/09_saturating/src/lib.rs index 4b0addec59..a254b434f3 100644 --- a/exercises/02_basic_calculator/09_saturating/src/lib.rs +++ b/exercises/02_basic_calculator/09_saturating/src/lib.rs @@ -1,9 +1,9 @@ pub fn factorial(n: u32) -> u32 { - let mut result = 1; + let mut result: u32 = 1; for i in 1..=n { // Use saturating multiplication to stop at the maximum value of u32 // rather than overflowing and wrapping around - result *= i; + result = result.saturating_mul(i); } result } diff --git a/exercises/02_basic_calculator/10_as_casting/src/lib.rs b/exercises/02_basic_calculator/10_as_casting/src/lib.rs index 2ba058c49a..60b01aa3c4 100644 --- a/exercises/02_basic_calculator/10_as_casting/src/lib.rs +++ b/exercises/02_basic_calculator/10_as_casting/src/lib.rs @@ -6,7 +6,7 @@ mod tests { #[test] fn u16_to_u32() { - let v: u32 = todo!(); + let v: u32 = 47; assert_eq!(47u16 as u32, v); } @@ -23,15 +23,15 @@ mod tests { // You could solve this by using exactly the same expression as above, // but that would defeat the purpose of the exercise. Instead, use a genuine - // `i8` value that is equivalent to `255` when converted to `u8`. - let y: i8 = todo!(); + // `i8` value that is equivalent to `255` when converted from `u8`. + let y: i8 = -1; assert_eq!(x, y); } #[test] fn bool_to_u8() { - let v: u8 = todo!(); + let v: u8 = 1; assert_eq!(true as u8, v); } } diff --git a/exercises/03_ticket_v1/00_intro/src/lib.rs b/exercises/03_ticket_v1/00_intro/src/lib.rs index f7afa4701e..c52e3ac9c2 100644 --- a/exercises/03_ticket_v1/00_intro/src/lib.rs +++ b/exercises/03_ticket_v1/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me πŸ‘‡ - "I'm ready to __!" + "I'm ready to start modelling a software ticket!" } #[cfg(test)] diff --git a/exercises/03_ticket_v1/01_struct/src/lib.rs b/exercises/03_ticket_v1/01_struct/src/lib.rs index 1119e330aa..68fbe46e01 100644 --- a/exercises/03_ticket_v1/01_struct/src/lib.rs +++ b/exercises/03_ticket_v1/01_struct/src/lib.rs @@ -4,6 +4,16 @@ // // It should also have a method named `is_available` that returns a `true` if the quantity is // greater than 0, otherwise `false`. +struct Order { + price: u32, + quantity: u32, +} + +impl Order { + fn is_available(&self) -> bool { + self.quantity > 0 + } +} #[cfg(test)] mod tests { diff --git a/exercises/03_ticket_v1/02_validation/src/lib.rs b/exercises/03_ticket_v1/02_validation/src/lib.rs index 7eaa5e5c6e..4fb9ed1e5b 100644 --- a/exercises/03_ticket_v1/02_validation/src/lib.rs +++ b/exercises/03_ticket_v1/02_validation/src/lib.rs @@ -18,7 +18,24 @@ impl Ticket { // as well as some `String` methods. Use the documentation of Rust's standard library // to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html fn new(title: String, description: String, status: String) -> Self { - todo!(); + if title.is_empty() { + panic!("Title cannot be empty"); + } + if title.len() > 50 { + panic!("Title cannot be longer than 50 bytes"); + } + + if description.is_empty() { + panic!("Description cannot be empty"); + } + if description.len() > 500 { + panic!("Description cannot be longer than 500 bytes"); + } + + if status != "To-Do" && status != "In Progress" && status != "Done" { + panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed"); + } + Self { title, description, diff --git a/exercises/03_ticket_v1/03_modules/src/lib.rs b/exercises/03_ticket_v1/03_modules/src/lib.rs index df0762094b..f93112b236 100644 --- a/exercises/03_ticket_v1/03_modules/src/lib.rs +++ b/exercises/03_ticket_v1/03_modules/src/lib.rs @@ -1,6 +1,7 @@ mod helpers { // TODO: Make this code compile, either by adding a `use` statement or by using // the appropriate path to refer to the `Ticket` struct. + use super::Ticket; fn create_todo_ticket(title: String, description: String) -> Ticket { Ticket::new(title, description, "To-Do".into()) diff --git a/exercises/03_ticket_v1/04_visibility/src/lib.rs b/exercises/03_ticket_v1/04_visibility/src/lib.rs index b494fc945b..65a15934f4 100644 --- a/exercises/03_ticket_v1/04_visibility/src/lib.rs +++ b/exercises/03_ticket_v1/04_visibility/src/lib.rs @@ -1,12 +1,12 @@ mod ticket { - struct Ticket { + pub struct Ticket { title: String, description: String, status: String, } impl Ticket { - fn new(title: String, description: String, status: String) -> Ticket { + pub fn new(title: String, description: String, status: String) -> Ticket { if title.is_empty() { panic!("Title cannot be empty"); } @@ -55,7 +55,7 @@ mod tests { // // TODO: Once you have verified that the below does not compile, // comment the line out to move on to the next exercise! - assert_eq!(ticket.description, "A description"); + // assert_eq!(ticket.description, "A description"); } fn encapsulation_cannot_be_violated() { @@ -68,10 +68,10 @@ mod tests { // // TODO: Once you have verified that the below does not compile, // comment the lines out to move on to the next exercise! - let ticket = Ticket { - title: "A title".into(), - description: "A description".into(), - status: "To-Do".into(), - }; + // let ticket = Ticket { + // title: "A title".into(), + // description: "A description".into(), + // status: "To-Do".into(), + // }; } } diff --git a/exercises/03_ticket_v1/05_encapsulation/src/lib.rs b/exercises/03_ticket_v1/05_encapsulation/src/lib.rs index 91e06eb2ab..e59d18db7a 100644 --- a/exercises/03_ticket_v1/05_encapsulation/src/lib.rs +++ b/exercises/03_ticket_v1/05_encapsulation/src/lib.rs @@ -34,6 +34,17 @@ pub mod ticket { // - `title` that returns the `title` field. // - `description` that returns the `description` field. // - `status` that returns the `status` field. + pub fn title(self) -> String { + self.title + } + + pub fn description(self) -> String { + self.description + } + + pub fn status(self) -> String { + self.status + } } } diff --git a/exercises/03_ticket_v1/06_ownership/src/lib.rs b/exercises/03_ticket_v1/06_ownership/src/lib.rs index ded2ad8de6..4ab49f1857 100644 --- a/exercises/03_ticket_v1/06_ownership/src/lib.rs +++ b/exercises/03_ticket_v1/06_ownership/src/lib.rs @@ -34,16 +34,16 @@ impl Ticket { } } - pub fn title(self) -> String { - self.title + pub fn title(&self) -> &String { + &self.title } - pub fn description(self) -> String { - self.description + pub fn description(&self) -> &String { + &self.description } - pub fn status(self) -> String { - self.status + pub fn status(&self) -> &String { + &self.status } } diff --git a/exercises/03_ticket_v1/07_setters/src/lib.rs b/exercises/03_ticket_v1/07_setters/src/lib.rs index e13ec87729..2ef2bbd2e2 100644 --- a/exercises/03_ticket_v1/07_setters/src/lib.rs +++ b/exercises/03_ticket_v1/07_setters/src/lib.rs @@ -11,21 +11,9 @@ pub struct Ticket { impl Ticket { pub fn new(title: String, description: String, status: String) -> Ticket { - if title.is_empty() { - panic!("Title cannot be empty"); - } - if title.len() > 50 { - panic!("Title cannot be longer than 50 bytes"); - } - if description.is_empty() { - panic!("Description cannot be empty"); - } - if description.len() > 500 { - panic!("Description cannot be longer than 500 bytes"); - } - if status != "To-Do" && status != "In Progress" && status != "Done" { - panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed"); - } + validate_title(&title); + validate_description(&description); + validate_status(&status); Ticket { title, @@ -45,6 +33,45 @@ impl Ticket { pub fn status(&self) -> &String { &self.status } + + pub fn set_title(&mut self, title: String) { + validate_title(&title); + self.title = title; + } + + pub fn set_description(&mut self, description: String) { + validate_description(&description); + self.description = description; + } + + pub fn set_status(&mut self, status: String) { + validate_status(&status); + self.status = status; + } +} + +fn validate_title(title: &String) { + if title.is_empty() { + panic!("Title cannot be empty"); + } + if title.len() > 50 { + panic!("Title cannot be longer than 50 bytes"); + } +} + +fn validate_description(description: &String) { + if description.is_empty() { + panic!("Description cannot be empty"); + } + if description.len() > 500 { + panic!("Description cannot be longer than 500 bytes"); + } +} + +fn validate_status(status: &String) { + if status != "To-Do" && status != "In Progress" && status != "Done" { + panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed"); + } } #[cfg(test)] diff --git a/exercises/03_ticket_v1/08_stack/src/lib.rs b/exercises/03_ticket_v1/08_stack/src/lib.rs index e97518cfac..ad7d2a491c 100644 --- a/exercises/03_ticket_v1/08_stack/src/lib.rs +++ b/exercises/03_ticket_v1/08_stack/src/lib.rs @@ -6,16 +6,16 @@ mod tests { #[test] fn u16_size() { - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 2); } #[test] fn i32_size() { - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 4); } #[test] fn bool_size() { - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 1); } } diff --git a/exercises/03_ticket_v1/09_heap/src/lib.rs b/exercises/03_ticket_v1/09_heap/src/lib.rs index 7273c2086a..5e24f6bc3c 100644 --- a/exercises/03_ticket_v1/09_heap/src/lib.rs +++ b/exercises/03_ticket_v1/09_heap/src/lib.rs @@ -13,7 +13,7 @@ mod tests { #[test] fn string_size() { - assert_eq!(size_of::(), todo!()); + assert_eq!(size_of::(), 24); } #[test] @@ -21,8 +21,8 @@ mod tests { // This is a tricky question! // The "intuitive" answer happens to be the correct answer this time, // but, in general, the memory layout of structs is a more complex topic. - // If you're curious, check out the "Type layout" section of The Rust Reference - // https://doc.rust-lang.org/reference/type-layout.html for more information. - assert_eq!(size_of::(), todo!()); + // If you're curious, check out the "Data layout" section of the Rustonomicon + // https://doc.rust-lang.org/nomicon/data.html for more information. + assert_eq!(size_of::(), 72); } } diff --git a/exercises/03_ticket_v1/10_references_in_memory/src/lib.rs b/exercises/03_ticket_v1/10_references_in_memory/src/lib.rs index d580758311..8fe236d8c6 100644 --- a/exercises/03_ticket_v1/10_references_in_memory/src/lib.rs +++ b/exercises/03_ticket_v1/10_references_in_memory/src/lib.rs @@ -11,18 +11,21 @@ mod tests { use super::Ticket; use std::mem::size_of; + // All numbers below assume you're running on a 64-bit system. + // If you're running on a 32-bit system, pointers will be 4 bytes long instead of 8. + #[test] fn u16_ref_size() { - assert_eq!(size_of::<&u16>(), todo!()); + assert_eq!(size_of::<&u16>(), 8); } #[test] fn u64_mut_ref_size() { - assert_eq!(size_of::<&mut u64>(), todo!()); + assert_eq!(size_of::<&mut u64>(), 8); } #[test] fn ticket_ref_size() { - assert_eq!(size_of::<&Ticket>(), todo!()); + assert_eq!(size_of::<&Ticket>(), 8); } } diff --git a/exercises/03_ticket_v1/11_destructor/src/lib.rs b/exercises/03_ticket_v1/11_destructor/src/lib.rs index 45bc89cf31..6555fbb589 100644 --- a/exercises/03_ticket_v1/11_destructor/src/lib.rs +++ b/exercises/03_ticket_v1/11_destructor/src/lib.rs @@ -2,7 +2,7 @@ // We'll pick the concept up again in a later chapter after covering traits and // interior mutability. fn outro() -> &'static str { - "I have a basic understanding of __!" + "I have a basic understanding of destructors!" } #[cfg(test)] diff --git a/exercises/03_ticket_v1/12_outro/src/lib.rs b/exercises/03_ticket_v1/12_outro/src/lib.rs index 15c99b83b5..e058670cbb 100644 --- a/exercises/03_ticket_v1/12_outro/src/lib.rs +++ b/exercises/03_ticket_v1/12_outro/src/lib.rs @@ -11,3 +11,74 @@ // Integration here has a very specific meaning: they test **the public API** of your project. // You'll need to pay attention to the visibility of your types and methods; integration // tests can't access private or `pub(crate)` items. +pub struct Order { + product_name: String, + quantity: u32, + unit_price: u32, +} + +impl Order { + pub fn new(product_name: String, quantity: u32, unit_price: u32) -> Order { + validate_product_name(&product_name); + validate_quantity(&quantity); + validate_unit_price(&unit_price); + + Order { + product_name, + quantity, + unit_price, + } + } + + pub fn product_name(&self) -> &String { + &self.product_name + } + + pub fn quantity(&self) -> &u32 { + &self.quantity + } + + pub fn unit_price(&self) -> &u32 { + &self.unit_price + } + + pub fn set_product_name(&mut self, product_name: String) { + validate_product_name(&product_name); + self.product_name = product_name; + } + + pub fn set_quantity(&mut self, quantity: u32) { + validate_quantity(&quantity); + self.quantity = quantity; + } + + pub fn set_unit_price(&mut self, unit_price: u32) { + validate_unit_price(&unit_price); + self.unit_price = unit_price; + } + + pub fn total(&self) -> u32 { + self.quantity * self.unit_price + } +} + +fn validate_product_name(product_name: &String) { + if product_name.is_empty() { + panic!("Product name cannot be empty"); + } + if product_name.len() > 300 { + panic!("Product name cannot be longer than 300 bytes"); + } +} + +fn validate_quantity(quantity: &u32) { + if quantity == &0 { + panic!("Quantity must be greater than zero"); + } +} + +fn validate_unit_price(unit_price: &u32) { + if unit_price == &0 { + panic!("Unit price must be greater than zero"); + } +} diff --git a/exercises/04_traits/00_intro/src/lib.rs b/exercises/04_traits/00_intro/src/lib.rs index 9513649ab7..1b0f8ee04b 100644 --- a/exercises/04_traits/00_intro/src/lib.rs +++ b/exercises/04_traits/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me πŸ‘‡ - "I'm ready to __!" + "I'm ready to learn about traits!" } #[cfg(test)] diff --git a/exercises/04_traits/01_trait/src/lib.rs b/exercises/04_traits/01_trait/src/lib.rs index 258eac507a..89c40ad6d8 100644 --- a/exercises/04_traits/01_trait/src/lib.rs +++ b/exercises/04_traits/01_trait/src/lib.rs @@ -2,6 +2,21 @@ // even, otherwise `false`. // // Then implement the trait for `u32` and `i32`. +pub trait IsEven { + fn is_even(&self) -> bool; +} + +impl IsEven for u32 { + fn is_even(&self) -> bool { + self % 2 == 0 + } +} + +impl IsEven for i32 { + fn is_even(&self) -> bool { + self % 2 == 0 + } +} #[cfg(test)] mod tests { diff --git a/exercises/04_traits/02_orphan_rule/src/lib.rs b/exercises/04_traits/02_orphan_rule/src/lib.rs index b707c10313..7927ba2de7 100644 --- a/exercises/04_traits/02_orphan_rule/src/lib.rs +++ b/exercises/04_traits/02_orphan_rule/src/lib.rs @@ -3,9 +3,3 @@ // a foreign type (`u32`, from `std`). // Look at the compiler error to get familiar with what it looks like. // Then delete the code below and move on to the next exercise. - -impl PartialEq for u32 { - fn eq(&self, _other: &Self) -> bool { - todo!() - } -} diff --git a/exercises/04_traits/03_operator_overloading/src/lib.rs b/exercises/04_traits/03_operator_overloading/src/lib.rs index b75c0f1de8..4a091d8c0e 100644 --- a/exercises/04_traits/03_operator_overloading/src/lib.rs +++ b/exercises/04_traits/03_operator_overloading/src/lib.rs @@ -8,7 +8,13 @@ struct Ticket { // TODO: Implement the `PartialEq` trait for `Ticket`. -impl PartialEq for Ticket {} +impl PartialEq for Ticket { + fn eq(&self, other: &Self) -> bool { + self.title == other.title + && self.description == other.description + && self.status == other.status + } +} #[cfg(test)] mod tests { diff --git a/exercises/04_traits/04_derive/src/lib.rs b/exercises/04_traits/04_derive/src/lib.rs index 0a74a26488..67d9024cda 100644 --- a/exercises/04_traits/04_derive/src/lib.rs +++ b/exercises/04_traits/04_derive/src/lib.rs @@ -8,7 +8,7 @@ // print both sides of the comparison to the terminal. // If the compared type doesn't implement `Debug`, it doesn't know how to represent them! -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] struct Ticket { title: String, description: String, diff --git a/exercises/04_traits/05_trait_bounds/src/lib.rs b/exercises/04_traits/05_trait_bounds/src/lib.rs index 10f0eb4c5e..7afdc38e62 100644 --- a/exercises/04_traits/05_trait_bounds/src/lib.rs +++ b/exercises/04_traits/05_trait_bounds/src/lib.rs @@ -6,7 +6,10 @@ // collections (e.g. BTreeMap). /// Return the minimum of two values. -pub fn min(left: T, right: T) -> T { +pub fn min(left: T, right: T) -> T { + // `Ord` guarantees that the values can be compared. + // `PartialOrd` would also make the compiler happy, but it would have different semantics: + // it'd either return the minimum value or `right` if they can't be compared. if left <= right { left } else { diff --git a/exercises/04_traits/06_str_slice/src/lib.rs b/exercises/04_traits/06_str_slice/src/lib.rs index 5bf66147c0..a37c0d4a68 100644 --- a/exercises/04_traits/06_str_slice/src/lib.rs +++ b/exercises/04_traits/06_str_slice/src/lib.rs @@ -31,15 +31,15 @@ impl Ticket { } } - pub fn title(&self) -> &String { + pub fn title(&self) -> &str { &self.title } - pub fn description(&self) -> &String { + pub fn description(&self) -> &str { &self.description } - pub fn status(&self) -> &String { + pub fn status(&self) -> &str { &self.status } } diff --git a/exercises/04_traits/07_deref/src/lib.rs b/exercises/04_traits/07_deref/src/lib.rs index c7a5c35b42..a21f9ef26b 100644 --- a/exercises/04_traits/07_deref/src/lib.rs +++ b/exercises/04_traits/07_deref/src/lib.rs @@ -12,11 +12,11 @@ pub struct Ticket { impl Ticket { pub fn title(&self) -> &str { - todo!() + self.title.trim() } pub fn description(&self) -> &str { - todo!() + self.description.trim() } } diff --git a/exercises/04_traits/08_sized/src/lib.rs b/exercises/04_traits/08_sized/src/lib.rs index a406fc5a83..0ebca5a622 100644 --- a/exercises/04_traits/08_sized/src/lib.rs +++ b/exercises/04_traits/08_sized/src/lib.rs @@ -3,5 +3,5 @@ pub fn example() { // via `std::mem::size_of` will result in a compile-time error. // // TODO: Comment out the following line and move on to the next exercise. - std::mem::size_of::(); + // std::mem::size_of::(); } diff --git a/exercises/04_traits/09_from/src/lib.rs b/exercises/04_traits/09_from/src/lib.rs index cc6f5b1bb7..b7995d2ec4 100644 --- a/exercises/04_traits/09_from/src/lib.rs +++ b/exercises/04_traits/09_from/src/lib.rs @@ -4,6 +4,12 @@ pub struct WrappingU32 { value: u32, } +impl From for WrappingU32 { + fn from(value: u32) -> Self { + WrappingU32 { value } + } +} + fn example() { let wrapping: WrappingU32 = 42.into(); let wrapping = WrappingU32::from(42); diff --git a/exercises/04_traits/10_assoc_vs_generic/src/lib.rs b/exercises/04_traits/10_assoc_vs_generic/src/lib.rs index 84f3e7b971..2193a4b50f 100644 --- a/exercises/04_traits/10_assoc_vs_generic/src/lib.rs +++ b/exercises/04_traits/10_assoc_vs_generic/src/lib.rs @@ -13,6 +13,36 @@ // You don't have to though: it's perfectly okay to write three separate // implementations manually. Venture further only if you're curious. +pub trait Power { + type Output; + + fn power(&self, n: Exponent) -> Self::Output; +} + +impl Power for u32 { + type Output = u32; + + fn power(&self, n: u16) -> Self::Output { + self.pow(n.into()) + } +} + +impl Power<&u32> for u32 { + type Output = u32; + + fn power(&self, n: &u32) -> Self::Output { + self.power(*n) + } +} + +impl Power for u32 { + type Output = u32; + + fn power(&self, n: u32) -> Self::Output { + self.pow(n) + } +} + #[cfg(test)] mod tests { use super::Power; diff --git a/exercises/04_traits/11_clone/src/lib.rs b/exercises/04_traits/11_clone/src/lib.rs index 4fbbe47369..8d93806d45 100644 --- a/exercises/04_traits/11_clone/src/lib.rs +++ b/exercises/04_traits/11_clone/src/lib.rs @@ -2,9 +2,10 @@ // to get the code to compile. pub fn summary(ticket: Ticket) -> (Ticket, Summary) { - (ticket, ticket.summary()) + (ticket.clone(), ticket.summary()) } +#[derive(Clone)] pub struct Ticket { pub title: String, pub description: String, diff --git a/exercises/04_traits/12_copy/src/lib.rs b/exercises/04_traits/12_copy/src/lib.rs index d74720b6f6..22090c463d 100644 --- a/exercises/04_traits/12_copy/src/lib.rs +++ b/exercises/04_traits/12_copy/src/lib.rs @@ -1,6 +1,7 @@ // TODO: implement the necessary traits to make the test compile and pass. // You *can't* modify the test. +#[derive(Debug, Clone, Copy, PartialEq)] pub struct WrappingU32 { value: u32, } @@ -11,6 +12,14 @@ impl WrappingU32 { } } +impl std::ops::Add for WrappingU32 { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self::new(self.value.wrapping_add(rhs.value)) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/04_traits/13_drop/src/lib.rs b/exercises/04_traits/13_drop/src/lib.rs index 2124d343a5..d60193e717 100644 --- a/exercises/04_traits/13_drop/src/lib.rs +++ b/exercises/04_traits/13_drop/src/lib.rs @@ -1,6 +1,27 @@ // TODO: implement a so-called "Drop bomb": a type that panics when dropped // unless a certain operation has been performed on it. // You can see the expected API in the tests below. +pub struct DropBomb { + defused: bool, +} + +impl DropBomb { + pub fn new() -> Self { + DropBomb { defused: false } + } + + pub fn defuse(&mut self) { + self.defused = true; + } +} + +impl Drop for DropBomb { + fn drop(&mut self) { + if !self.defused { + panic!("Boom!"); + } + } +} #[cfg(test)] mod tests { diff --git a/exercises/04_traits/14_outro/src/lib.rs b/exercises/04_traits/14_outro/src/lib.rs index 547fd942b9..7393296757 100644 --- a/exercises/04_traits/14_outro/src/lib.rs +++ b/exercises/04_traits/14_outro/src/lib.rs @@ -8,3 +8,75 @@ // It should be possible to print its debug representation. // // Tests are located in the `tests` folderβ€”pay attention to the visibility of your types and methods. + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct SaturatingU16 { + value: u16, +} + +impl From for SaturatingU16 { + fn from(value: u16) -> Self { + Self { value } + } +} + +impl From for SaturatingU16 { + fn from(value: u8) -> Self { + Self { + value: value.into(), + } + } +} + +impl From<&u16> for SaturatingU16 { + fn from(value: &u16) -> Self { + (*value).into() + } +} + +impl From<&u8> for SaturatingU16 { + fn from(value: &u8) -> Self { + (*value).into() + } +} + +impl std::ops::Add for SaturatingU16 { + type Output = SaturatingU16; + + fn add(self, rhs: Self) -> Self::Output { + self + rhs.value + } +} + +impl std::ops::Add<&SaturatingU16> for SaturatingU16 { + type Output = SaturatingU16; + + fn add(self, rhs: &SaturatingU16) -> Self::Output { + self + *rhs + } +} + +impl std::ops::Add for SaturatingU16 { + type Output = Self; + + fn add(self, rhs: u16) -> Self::Output { + let sum = self.value.saturating_add(rhs); + Self { + value: sum, + } + } +} + +impl std::ops::Add<&u16> for SaturatingU16 { + type Output = SaturatingU16; + + fn add(self, rhs: &u16) -> Self::Output { + self + *rhs + } +} + +impl PartialEq for SaturatingU16 { + fn eq(&self, other: &u16) -> bool { + self.value == *other + } +} diff --git a/exercises/05_ticket_v2/00_intro/src/lib.rs b/exercises/05_ticket_v2/00_intro/src/lib.rs index ce1f75fc0d..ff614d11f7 100644 --- a/exercises/05_ticket_v2/00_intro/src/lib.rs +++ b/exercises/05_ticket_v2/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me πŸ‘‡ - "I'm ready to __!" + "I'm ready to refine the `Ticket` type!" } #[cfg(test)] diff --git a/exercises/05_ticket_v2/01_enum/src/lib.rs b/exercises/05_ticket_v2/01_enum/src/lib.rs index a3d9592939..d1a2d1523e 100644 --- a/exercises/05_ticket_v2/01_enum/src/lib.rs +++ b/exercises/05_ticket_v2/01_enum/src/lib.rs @@ -7,15 +7,18 @@ struct Ticket { title: String, description: String, - status: String, + status: Status, } +#[derive(Debug, PartialEq, Copy, Clone)] enum Status { - // TODO: add the missing variants + ToDo, + InProgress, + Done, } impl Ticket { - pub fn new(title: String, description: String, status: String) -> Ticket { + pub fn new(title: String, description: String, status: Status) -> Ticket { if title.is_empty() { panic!("Title cannot be empty"); } @@ -28,9 +31,6 @@ impl Ticket { if description.len() > 500 { panic!("Description cannot be longer than 500 bytes"); } - if status != "To-Do" && status != "In Progress" && status != "Done" { - panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed"); - } Ticket { title, @@ -47,7 +47,7 @@ impl Ticket { &self.description } - pub fn status(&self) -> &String { + pub fn status(&self) -> &Status { &self.status } } diff --git a/exercises/05_ticket_v2/02_match/src/lib.rs b/exercises/05_ticket_v2/02_match/src/lib.rs index d30c569ceb..230d55c0ce 100644 --- a/exercises/05_ticket_v2/02_match/src/lib.rs +++ b/exercises/05_ticket_v2/02_match/src/lib.rs @@ -9,7 +9,12 @@ enum Shape { impl Shape { // TODO: Implement the `n_sides` method using a `match`. pub fn n_sides(&self) -> u8 { - todo!() + match self { + Shape::Circle => 0, + Shape::Square | Shape::Rectangle => 4, + Shape::Triangle => 3, + Shape::Pentagon => 5, + } } } diff --git a/exercises/05_ticket_v2/03_variants_with_data/src/lib.rs b/exercises/05_ticket_v2/03_variants_with_data/src/lib.rs index 03faf0cd3d..b9376e1a7d 100644 --- a/exercises/05_ticket_v2/03_variants_with_data/src/lib.rs +++ b/exercises/05_ticket_v2/03_variants_with_data/src/lib.rs @@ -38,7 +38,10 @@ impl Ticket { } } pub fn assigned_to(&self) -> &str { - todo!() + match &self.status { + Status::InProgress { assigned_to } => assigned_to, + _ => panic!("Only `In-Progress` tickets can be assigned to someone"), + } } } diff --git a/exercises/05_ticket_v2/04_if_let/src/lib.rs b/exercises/05_ticket_v2/04_if_let/src/lib.rs index 0884fb578e..0de315cbd3 100644 --- a/exercises/05_ticket_v2/04_if_let/src/lib.rs +++ b/exercises/05_ticket_v2/04_if_let/src/lib.rs @@ -8,7 +8,11 @@ impl Shape { // TODO: Implement the `radius` method using // either an `if let` or a `let/else`. pub fn radius(&self) -> f64 { - todo!() + if let Shape::Circle { radius } = self { + *radius + } else { + panic!("Not a circle"); + } } } diff --git a/exercises/05_ticket_v2/05_nullability/src/lib.rs b/exercises/05_ticket_v2/05_nullability/src/lib.rs index f4e5cb2c09..22a283bdfd 100644 --- a/exercises/05_ticket_v2/05_nullability/src/lib.rs +++ b/exercises/05_ticket_v2/05_nullability/src/lib.rs @@ -36,7 +36,10 @@ impl Ticket { } } pub fn assigned_to(&self) -> Option<&String> { - todo!() + match &self.status { + Status::InProgress { assigned_to } => Some(assigned_to), + _ => None, + } } } diff --git a/exercises/05_ticket_v2/06_fallibility/src/lib.rs b/exercises/05_ticket_v2/06_fallibility/src/lib.rs index 3144bee9ad..096628b8a5 100644 --- a/exercises/05_ticket_v2/06_fallibility/src/lib.rs +++ b/exercises/05_ticket_v2/06_fallibility/src/lib.rs @@ -16,25 +16,25 @@ enum Status { } impl Ticket { - pub fn new(title: String, description: String, status: Status) -> Ticket { + pub fn new(title: String, description: String, status: Status) -> Result { if title.is_empty() { - panic!("Title cannot be empty"); + return Err("Title cannot be empty".into()); } if title.len() > 50 { - panic!("Title cannot be longer than 50 bytes"); + return Err("Title cannot be longer than 50 bytes".into()); } if description.is_empty() { - panic!("Description cannot be empty"); + return Err("Description cannot be empty".into()); } if description.len() > 500 { - panic!("Description cannot be longer than 500 bytes"); + return Err("Description cannot be longer than 500 bytes".into()); } - Ticket { + Ok(Ticket { title, description, status, - } + }) } } diff --git a/exercises/05_ticket_v2/07_unwrap/src/lib.rs b/exercises/05_ticket_v2/07_unwrap/src/lib.rs index 4b6419bad9..fcf1e20d25 100644 --- a/exercises/05_ticket_v2/07_unwrap/src/lib.rs +++ b/exercises/05_ticket_v2/07_unwrap/src/lib.rs @@ -2,7 +2,16 @@ // When the description is invalid, instead, it should use a default description: // "Description not provided". fn easy_ticket(title: String, description: String, status: Status) -> Ticket { - todo!() + match Ticket::new(title.clone(), description, status.clone()) { + Ok(ticket) => ticket, + Err(error) => { + if error.contains("Description") { + Ticket::new(title, "Description not provided".to_string(), status).unwrap() + } else { + panic!("{error}"); + } + } + } } #[derive(Debug, PartialEq, Clone)] diff --git a/exercises/05_ticket_v2/08_error_enums/src/lib.rs b/exercises/05_ticket_v2/08_error_enums/src/lib.rs index c74fcc9ad6..2f77d96bfa 100644 --- a/exercises/05_ticket_v2/08_error_enums/src/lib.rs +++ b/exercises/05_ticket_v2/08_error_enums/src/lib.rs @@ -1,14 +1,24 @@ // TODO: Use two variants, one for a title error and one for a description error. // Each variant should contain a string with the explanation of what went wrong exactly. // You'll have to update the implementation of `Ticket::new` as well. -enum TicketNewError {} +#[derive(Debug)] +enum TicketNewError { + TitleError(String), + DescriptionError(String), +} // TODO: `easy_ticket` should panic when the title is invalid, using the error message // stored inside the relevant variant of the `TicketNewError` enum. // When the description is invalid, instead, it should use a default description: // "Description not provided". fn easy_ticket(title: String, description: String, status: Status) -> Ticket { - todo!() + match Ticket::new(title.clone(), description, status.clone()) { + Ok(ticket) => ticket, + Err(TicketNewError::DescriptionError(_)) => { + Ticket::new(title, "Description not provided".to_string(), status).unwrap() + } + Err(TicketNewError::TitleError(error)) => panic!("{error}"), + } } #[derive(Debug, PartialEq)] @@ -32,16 +42,24 @@ impl Ticket { status: Status, ) -> Result { if title.is_empty() { - return Err("Title cannot be empty".to_string()); + return Err(TicketNewError::TitleError( + "Title cannot be empty".to_string(), + )); } if title.len() > 50 { - return Err("Title cannot be longer than 50 bytes".to_string()); + return Err(TicketNewError::TitleError( + "Title cannot be longer than 50 bytes".to_string(), + )); } if description.is_empty() { - return Err("Description cannot be empty".to_string()); + return Err(TicketNewError::DescriptionError( + "Description cannot be empty".to_string(), + )); } if description.len() > 500 { - return Err("Description cannot be longer than 500 bytes".to_string()); + return Err(TicketNewError::DescriptionError( + "Description cannot be longer than 500 bytes".to_string(), + )); } Ok(Ticket { diff --git a/exercises/05_ticket_v2/09_error_trait/src/lib.rs b/exercises/05_ticket_v2/09_error_trait/src/lib.rs index 68b5769022..86cffc6bca 100644 --- a/exercises/05_ticket_v2/09_error_trait/src/lib.rs +++ b/exercises/05_ticket_v2/09_error_trait/src/lib.rs @@ -3,17 +3,37 @@ // The docs for the `std::fmt` module are a good place to start and look for examples: // https://doc.rust-lang.org/std/fmt/index.html#write +#[derive(Debug)] enum TicketNewError { TitleError(String), DescriptionError(String), } +impl std::fmt::Display for TicketNewError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + TicketNewError::TitleError(msg) => write!(f, "{}", msg), + TicketNewError::DescriptionError(msg) => write!(f, "{}", msg), + } + } +} + +impl std::error::Error for TicketNewError {} + // TODO: `easy_ticket` should panic when the title is invalid, using the error message // stored inside the relevant variant of the `TicketNewError` enum. // When the description is invalid, instead, it should use a default description: // "Description not provided". fn easy_ticket(title: String, description: String, status: Status) -> Ticket { - todo!() + match Ticket::new(title.clone(), description, status.clone()) { + Ok(ticket) => ticket, + Err(err) => match err { + TicketNewError::TitleError(_) => panic!("{err}"), + TicketNewError::DescriptionError(_) => { + Ticket::new(title, "Description not provided".to_string(), status).unwrap() + } + }, + } } #[derive(Debug, PartialEq, Clone)] diff --git a/exercises/05_ticket_v2/10_packages/src/lib.rs b/exercises/05_ticket_v2/10_packages/src/lib.rs new file mode 100644 index 0000000000..7d83484ea9 --- /dev/null +++ b/exercises/05_ticket_v2/10_packages/src/lib.rs @@ -0,0 +1,3 @@ +pub fn hello_world() { + println!("Hello, world!"); +} diff --git a/exercises/05_ticket_v2/11_dependencies/Cargo.toml b/exercises/05_ticket_v2/11_dependencies/Cargo.toml index c18abf92a8..f56c4207ec 100644 --- a/exercises/05_ticket_v2/11_dependencies/Cargo.toml +++ b/exercises/05_ticket_v2/11_dependencies/Cargo.toml @@ -2,3 +2,6 @@ name = "deps" version = "0.1.0" edition = "2021" + +[dependencies] +anyhow = "1.0.82" diff --git a/exercises/05_ticket_v2/12_thiserror/Cargo.toml b/exercises/05_ticket_v2/12_thiserror/Cargo.toml index fb9c7d5e3c..02ff2d7178 100644 --- a/exercises/05_ticket_v2/12_thiserror/Cargo.toml +++ b/exercises/05_ticket_v2/12_thiserror/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +thiserror = "1" [dev-dependencies] common = { path = "../../../helpers/common" } diff --git a/exercises/05_ticket_v2/12_thiserror/src/lib.rs b/exercises/05_ticket_v2/12_thiserror/src/lib.rs index 9289143a5c..a919f9b473 100644 --- a/exercises/05_ticket_v2/12_thiserror/src/lib.rs +++ b/exercises/05_ticket_v2/12_thiserror/src/lib.rs @@ -3,10 +3,15 @@ // a `String` field into each variant. // You'll also have to add `thiserror` as a dependency in the `Cargo.toml` file. +#[derive(Debug, thiserror::Error)] enum TicketNewError { + #[error("Title cannot be empty")] TitleCannotBeEmpty, + #[error("Title cannot be longer than 50 bytes")] TitleTooLong, + #[error("Description cannot be empty")] DescriptionCannotBeEmpty, + #[error("Description cannot be longer than 500 bytes")] DescriptionTooLong, } diff --git a/exercises/05_ticket_v2/13_try_from/Cargo.toml b/exercises/05_ticket_v2/13_try_from/Cargo.toml index 5d7035797d..1b3046e6c5 100644 --- a/exercises/05_ticket_v2/13_try_from/Cargo.toml +++ b/exercises/05_ticket_v2/13_try_from/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +thiserror = "1.0.59" diff --git a/exercises/05_ticket_v2/13_try_from/src/lib.rs b/exercises/05_ticket_v2/13_try_from/src/lib.rs index e0e1115f9d..cdcb02c2b3 100644 --- a/exercises/05_ticket_v2/13_try_from/src/lib.rs +++ b/exercises/05_ticket_v2/13_try_from/src/lib.rs @@ -8,6 +8,35 @@ enum Status { Done, } +#[derive(Debug, thiserror::Error)] +#[error("{invalid_status} is not a valid status")] +struct ParseStatusError { + invalid_status: String, +} + +impl TryFrom for Status { + type Error = ParseStatusError; + + fn try_from(value: String) -> Result { + value.as_str().try_into() + } +} + +impl TryFrom<&str> for Status { + type Error = ParseStatusError; + + fn try_from(value: &str) -> Result { + match value.to_lowercase().as_str() { + "todo" => Ok(Status::ToDo), + "inprogress" => Ok(Status::InProgress), + "done" => Ok(Status::Done), + _ => Err(ParseStatusError { + invalid_status: value.to_string(), + }), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/05_ticket_v2/14_source/src/lib.rs b/exercises/05_ticket_v2/14_source/src/lib.rs index 9f7cce55bf..f9fb6e77d5 100644 --- a/exercises/05_ticket_v2/14_source/src/lib.rs +++ b/exercises/05_ticket_v2/14_source/src/lib.rs @@ -1,4 +1,4 @@ -use crate::status::Status; +use crate::status::{ParseStatusError, Status}; // We've seen how to declare modules in one of the earliest exercises, but // we haven't seen how to extract them into separate files. @@ -23,6 +23,8 @@ pub enum TicketNewError { DescriptionCannotBeEmpty, #[error("Description cannot be longer than 500 bytes")] DescriptionTooLong, + #[error("{0}")] + InvalidStatus(#[from] ParseStatusError), } #[derive(Debug, PartialEq, Clone)] @@ -48,6 +50,7 @@ impl Ticket { } // TODO: Parse the status string into a `Status` enum. + let status = Status::try_from(status)?; Ok(Ticket { title, diff --git a/exercises/05_ticket_v2/15_outro/Cargo.toml b/exercises/05_ticket_v2/15_outro/Cargo.toml index dc32a4118f..2735220ce3 100644 --- a/exercises/05_ticket_v2/15_outro/Cargo.toml +++ b/exercises/05_ticket_v2/15_outro/Cargo.toml @@ -2,3 +2,6 @@ name = "outro_04" version = "0.1.0" edition = "2021" + +[dependencies] +thiserror = "1.0.59" diff --git a/exercises/05_ticket_v2/15_outro/src/description.rs b/exercises/05_ticket_v2/15_outro/src/description.rs index ecf55ad19d..35935ceadf 100644 --- a/exercises/05_ticket_v2/15_outro/src/description.rs +++ b/exercises/05_ticket_v2/15_outro/src/description.rs @@ -1,9 +1,45 @@ // TODO: Implement `TryFrom` and `TryFrom<&str>` for the `TicketDescription` type, // enforcing that the description is not empty and is not longer than 500 bytes. // Implement the traits required to make the tests pass too. - +#[derive(Debug, PartialEq, Clone)] pub struct TicketDescription(String); +#[derive(Debug, thiserror::Error)] +pub enum TicketDescriptionError { + #[error("The description cannot be empty")] + Empty, + #[error("The description cannot be longer than 500 bytes")] + TooLong, +} + +impl TryFrom for TicketDescription { + type Error = TicketDescriptionError; + + fn try_from(value: String) -> Result { + validate(&value)?; + Ok(Self(value)) + } +} + +impl TryFrom<&str> for TicketDescription { + type Error = TicketDescriptionError; + + fn try_from(value: &str) -> Result { + validate(value)?; + Ok(Self(value.to_string())) + } +} + +fn validate(description: &str) -> Result<(), TicketDescriptionError> { + if description.is_empty() { + Err(TicketDescriptionError::Empty) + } else if description.len() > 500 { + Err(TicketDescriptionError::TooLong) + } else { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/05_ticket_v2/15_outro/src/status.rs b/exercises/05_ticket_v2/15_outro/src/status.rs index 4453d524f8..0611887d29 100644 --- a/exercises/05_ticket_v2/15_outro/src/status.rs +++ b/exercises/05_ticket_v2/15_outro/src/status.rs @@ -1,12 +1,43 @@ // TODO: Implement `TryFrom` and `TryFrom<&str>` for the `Status` enum. // The parsing should be case-insensitive. +#[derive(Debug, PartialEq, Clone)] pub enum Status { ToDo, InProgress, Done, } +impl TryFrom for Status { + type Error = ParseStatusError; + + fn try_from(value: String) -> Result { + let value = value.to_lowercase(); + match value.as_str() { + "todo" => Ok(Status::ToDo), + "inprogress" => Ok(Status::InProgress), + "done" => Ok(Status::Done), + _ => Err(ParseStatusError { + invalid_status: value, + }), + } + } +} + +impl TryFrom<&str> for Status { + type Error = ParseStatusError; + + fn try_from(value: &str) -> Result { + value.to_lowercase().try_into() + } +} + +#[derive(Debug, thiserror::Error)] +#[error("`{invalid_status}` is not a valid status. Use one of: ToDo, InProgress, Done")] +pub struct ParseStatusError { + invalid_status: String, +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/05_ticket_v2/15_outro/src/title.rs b/exercises/05_ticket_v2/15_outro/src/title.rs index 7c26c934c8..896fab5b52 100644 --- a/exercises/05_ticket_v2/15_outro/src/title.rs +++ b/exercises/05_ticket_v2/15_outro/src/title.rs @@ -2,8 +2,45 @@ // enforcing that the title is not empty and is not longer than 50 bytes. // Implement the traits required to make the tests pass too. +#[derive(Debug, PartialEq, Clone)] pub struct TicketTitle(String); +#[derive(Debug, thiserror::Error)] +pub enum TicketTitleError { + #[error("The title cannot be empty")] + Empty, + #[error("The title cannot be longer than 50 bytes")] + TooLong, +} + +impl TryFrom for TicketTitle { + type Error = TicketTitleError; + + fn try_from(value: String) -> Result { + validate(&value)?; + Ok(Self(value)) + } +} + +impl TryFrom<&str> for TicketTitle { + type Error = TicketTitleError; + + fn try_from(value: &str) -> Result { + validate(value)?; + Ok(Self(value.to_string())) + } +} + +fn validate(title: &str) -> Result<(), TicketTitleError> { + if title.is_empty() { + Err(TicketTitleError::Empty) + } else if title.len() > 50 { + Err(TicketTitleError::TooLong) + } else { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/06_ticket_management/00_intro/src/lib.rs b/exercises/06_ticket_management/00_intro/src/lib.rs index 118e48375d..d0fd0dab13 100644 --- a/exercises/06_ticket_management/00_intro/src/lib.rs +++ b/exercises/06_ticket_management/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me πŸ‘‡ - "I'm ready to __!" + "I'm ready to build a ticket management system!" } #[cfg(test)] diff --git a/exercises/06_ticket_management/01_arrays/src/lib.rs b/exercises/06_ticket_management/01_arrays/src/lib.rs index e06cb2b2d9..58e3bd3ad1 100644 --- a/exercises/06_ticket_management/01_arrays/src/lib.rs +++ b/exercises/06_ticket_management/01_arrays/src/lib.rs @@ -1,7 +1,7 @@ // TODO: Flesh out the `WeekTemperatures` struct and its method implementations to pass the tests. pub struct WeekTemperatures { - // TODO + temperatures: [Option; 7], } pub enum Weekday { @@ -16,15 +16,31 @@ pub enum Weekday { impl WeekTemperatures { pub fn new() -> Self { - todo!() + WeekTemperatures { + temperatures: [None; 7], + } } pub fn get_temperature(&self, day: Weekday) -> Option { - todo!() + let index = weekday2index(&day); + self.temperatures[index] } pub fn set_temperature(&mut self, day: Weekday, temperature: i32) { - todo!() + let index = weekday2index(&day); + self.temperatures[index] = Some(temperature); + } +} + +fn weekday2index(day: &Weekday) -> usize { + match day { + Weekday::Monday => 0, + Weekday::Tuesday => 1, + Weekday::Wednesday => 2, + Weekday::Thursday => 3, + Weekday::Friday => 4, + Weekday::Saturday => 5, + Weekday::Sunday => 6, } } diff --git a/exercises/06_ticket_management/02_vec/src/lib.rs b/exercises/06_ticket_management/02_vec/src/lib.rs index 2e8b4985b3..41685da3c3 100644 --- a/exercises/06_ticket_management/02_vec/src/lib.rs +++ b/exercises/06_ticket_management/02_vec/src/lib.rs @@ -11,11 +11,12 @@ // We expect `fibonacci(0)` to return `0`, `fibonacci(1)` to return `1`, // `fibonacci(2)` to return `1`, and so on. pub fn fibonacci(n: u32) -> u32 { - // TODO: implement the `fibonacci` function - // - // Hint: use a `Vec` to memoize the results you have already calculated - // so that you don't have to recalculate them several times. - todo!() + let n = n as usize; + let mut memo = vec![0, 1]; + for i in 2..=n { + memo.push(memo[i - 1] + memo[i - 2]); + } + memo[n] } #[cfg(test)] diff --git a/exercises/06_ticket_management/03_resizing/src/lib.rs b/exercises/06_ticket_management/03_resizing/src/lib.rs index 000ca2d84b..f29eb7ef2b 100644 --- a/exercises/06_ticket_management/03_resizing/src/lib.rs +++ b/exercises/06_ticket_management/03_resizing/src/lib.rs @@ -12,6 +12,6 @@ mod tests { // Can you guess what the new capacity will be? // Beware that the standard library makes no guarantees about the // algorithm used to resize the vector, so this may change in the future. - assert_eq!(v.capacity(), todo!()); + assert_eq!(v.capacity(), 4); } } diff --git a/exercises/06_ticket_management/04_iterators/src/lib.rs b/exercises/06_ticket_management/04_iterators/src/lib.rs index e7eb9f9120..b76e858317 100644 --- a/exercises/06_ticket_management/04_iterators/src/lib.rs +++ b/exercises/06_ticket_management/04_iterators/src/lib.rs @@ -39,6 +39,15 @@ impl TicketStore { } } +impl IntoIterator for TicketStore { + type Item = Ticket; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.tickets.into_iter() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/06_ticket_management/05_iter/src/lib.rs b/exercises/06_ticket_management/05_iter/src/lib.rs index 71d3e51c62..89e04e7e25 100644 --- a/exercises/06_ticket_management/05_iter/src/lib.rs +++ b/exercises/06_ticket_management/05_iter/src/lib.rs @@ -34,6 +34,10 @@ impl TicketStore { pub fn add_ticket(&mut self, ticket: Ticket) { self.tickets.push(ticket); } + + pub fn iter(&self) -> std::slice::Iter { + self.tickets.iter() + } } #[cfg(test)] diff --git a/exercises/06_ticket_management/06_lifetimes/src/lib.rs b/exercises/06_ticket_management/06_lifetimes/src/lib.rs index 545ed75cc4..68a87a6b7b 100644 --- a/exercises/06_ticket_management/06_lifetimes/src/lib.rs +++ b/exercises/06_ticket_management/06_lifetimes/src/lib.rs @@ -36,6 +36,15 @@ impl TicketStore { } } +impl<'a> IntoIterator for &'a TicketStore { + type Item = &'a Ticket; + type IntoIter = std::slice::Iter<'a, Ticket>; + + fn into_iter(self) -> Self::IntoIter { + self.tickets.iter() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/06_ticket_management/07_combinators/src/lib.rs b/exercises/06_ticket_management/07_combinators/src/lib.rs index 2731211852..75af38c5ee 100644 --- a/exercises/06_ticket_management/07_combinators/src/lib.rs +++ b/exercises/06_ticket_management/07_combinators/src/lib.rs @@ -31,6 +31,13 @@ impl TicketStore { pub fn add_ticket(&mut self, ticket: Ticket) { self.tickets.push(ticket); } + + pub fn to_dos(&self) -> Vec<&Ticket> { + self.tickets + .iter() + .filter(|ticket| ticket.status == Status::ToDo) + .collect() + } } #[cfg(test)] diff --git a/exercises/06_ticket_management/08_impl_trait/src/lib.rs b/exercises/06_ticket_management/08_impl_trait/src/lib.rs index d8afbe01d7..922ee771c3 100644 --- a/exercises/06_ticket_management/08_impl_trait/src/lib.rs +++ b/exercises/06_ticket_management/08_impl_trait/src/lib.rs @@ -31,6 +31,12 @@ impl TicketStore { pub fn add_ticket(&mut self, ticket: Ticket) { self.tickets.push(ticket); } + + pub fn in_progress(&self) -> impl Iterator { + self.tickets + .iter() + .filter(|ticket| ticket.status == Status::InProgress) + } } #[cfg(test)] diff --git a/exercises/06_ticket_management/09_impl_trait_2/src/lib.rs b/exercises/06_ticket_management/09_impl_trait_2/src/lib.rs index 33e982b2af..c080376c98 100644 --- a/exercises/06_ticket_management/09_impl_trait_2/src/lib.rs +++ b/exercises/06_ticket_management/09_impl_trait_2/src/lib.rs @@ -33,7 +33,7 @@ impl TicketStore { // that can be infallibly converted into a `Ticket`. // This can make it nicer to use the method, as it removes the syntax noise of `.into()` // from the calling site. It can worsen the quality of the compiler error messages, though. - pub fn add_ticket(&mut self, ticket: impl Into) { + pub fn add_ticket>(&mut self, ticket: T) { self.tickets.push(ticket.into()); } } diff --git a/exercises/06_ticket_management/10_slices/src/lib.rs b/exercises/06_ticket_management/10_slices/src/lib.rs index 1d2c6f7f00..ffb54f8954 100644 --- a/exercises/06_ticket_management/10_slices/src/lib.rs +++ b/exercises/06_ticket_management/10_slices/src/lib.rs @@ -1,6 +1,10 @@ // TODO: Define a function named `sum` that takes a reference to a slice of `u32` and returns the sum of all // elements in the slice. +pub fn sum(slice: &[u32]) -> u32 { + slice.iter().sum() +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/06_ticket_management/11_mutable_slices/src/lib.rs b/exercises/06_ticket_management/11_mutable_slices/src/lib.rs index 4ba888c790..593fc4f547 100644 --- a/exercises/06_ticket_management/11_mutable_slices/src/lib.rs +++ b/exercises/06_ticket_management/11_mutable_slices/src/lib.rs @@ -1,6 +1,12 @@ // TODO: Define a function named `squared` that raises all `i32`s within a slice to the power of 2. // The slice should be modified in place. +pub fn squared(slice: &mut [i32]) { + for i in slice.iter_mut() { + *i *= *i; + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/06_ticket_management/12_two_states/src/lib.rs b/exercises/06_ticket_management/12_two_states/src/lib.rs index 10b5cb1d35..e51934c10a 100644 --- a/exercises/06_ticket_management/12_two_states/src/lib.rs +++ b/exercises/06_ticket_management/12_two_states/src/lib.rs @@ -11,6 +11,7 @@ use ticket_fields::{TicketDescription, TicketTitle}; #[derive(Clone)] pub struct TicketStore { tickets: Vec, + counter: u64, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -41,11 +42,25 @@ impl TicketStore { pub fn new() -> Self { Self { tickets: Vec::new(), + counter: 0, } } - pub fn add_ticket(&mut self, ticket: Ticket) { + pub fn add_ticket(&mut self, draft: TicketDraft) -> TicketId { + let id = self.counter; + self.counter += 1; + let ticket = Ticket { + id: TicketId(id), + title: draft.title, + description: draft.description, + status: Status::ToDo, + }; self.tickets.push(ticket); + TicketId(id) + } + + pub fn get(&self, id: TicketId) -> Option<&Ticket> { + self.tickets.iter().find(|ticket| ticket.id == id) } } diff --git a/exercises/06_ticket_management/13_index/src/lib.rs b/exercises/06_ticket_management/13_index/src/lib.rs index 0b08cc11b5..ea27bcfbdc 100644 --- a/exercises/06_ticket_management/13_index/src/lib.rs +++ b/exercises/06_ticket_management/13_index/src/lib.rs @@ -1,5 +1,6 @@ // TODO: Implement `Index<&TicketId>` and `Index` for `TicketStore`. +use std::ops::Index; use ticket_fields::{TicketDescription, TicketTitle}; #[derive(Clone)] @@ -58,6 +59,22 @@ impl TicketStore { } } +impl Index for TicketStore { + type Output = Ticket; + + fn index(&self, id: TicketId) -> &Self::Output { + self.tickets.iter().find(|&t| t.id == id).unwrap() + } +} + +impl Index<&TicketId> for TicketStore { + type Output = Ticket; + + fn index(&self, id: &TicketId) -> &Self::Output { + &self[*id] + } +} + #[cfg(test)] mod tests { use crate::{Status, TicketDraft, TicketStore}; diff --git a/exercises/06_ticket_management/14_index_mut/src/lib.rs b/exercises/06_ticket_management/14_index_mut/src/lib.rs index fa3945160c..aba27d64d0 100644 --- a/exercises/06_ticket_management/14_index_mut/src/lib.rs +++ b/exercises/06_ticket_management/14_index_mut/src/lib.rs @@ -1,6 +1,6 @@ // TODO: Implement `IndexMut<&TicketId>` and `IndexMut` for `TicketStore`. -use std::ops::Index; +use std::ops::{Index, IndexMut}; use ticket_fields::{TicketDescription, TicketTitle}; #[derive(Clone)] @@ -75,6 +75,18 @@ impl Index<&TicketId> for TicketStore { } } +impl IndexMut for TicketStore { + fn index_mut(&mut self, index: TicketId) -> &mut Self::Output { + self.tickets.iter_mut().find(|t| t.id == index).unwrap() + } +} + +impl IndexMut<&TicketId> for TicketStore { + fn index_mut(&mut self, index: &TicketId) -> &mut Self::Output { + &mut self[*index] + } +} + #[cfg(test)] mod tests { use crate::{Status, TicketDraft, TicketStore}; diff --git a/exercises/06_ticket_management/15_hashmap/src/lib.rs b/exercises/06_ticket_management/15_hashmap/src/lib.rs index ea109a6fef..c50fb57dda 100644 --- a/exercises/06_ticket_management/15_hashmap/src/lib.rs +++ b/exercises/06_ticket_management/15_hashmap/src/lib.rs @@ -11,7 +11,7 @@ pub struct TicketStore { counter: u64, } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct TicketId(u64); #[derive(Clone, Debug, PartialEq)] @@ -38,7 +38,7 @@ pub enum Status { impl TicketStore { pub fn new() -> Self { Self { - tickets: todo!(), + tickets: HashMap::new(), counter: 0, } } @@ -52,16 +52,16 @@ impl TicketStore { description: ticket.description, status: Status::ToDo, }; - todo!(); + self.tickets.insert(id, ticket); id } pub fn get(&self, id: TicketId) -> Option<&Ticket> { - todo!() + self.tickets.get(&id) } pub fn get_mut(&mut self, id: TicketId) -> Option<&mut Ticket> { - todo!() + self.tickets.get_mut(&id) } } diff --git a/exercises/06_ticket_management/16_btreemap/src/lib.rs b/exercises/06_ticket_management/16_btreemap/src/lib.rs index d9af95d2d8..ddbc4322de 100644 --- a/exercises/06_ticket_management/16_btreemap/src/lib.rs +++ b/exercises/06_ticket_management/16_btreemap/src/lib.rs @@ -13,7 +13,7 @@ pub struct TicketStore { counter: u64, } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Ord, PartialOrd, Eq)] pub struct TicketId(u64); #[derive(Clone, Debug, PartialEq)] @@ -40,7 +40,7 @@ pub enum Status { impl TicketStore { pub fn new() -> Self { Self { - tickets: todo!(), + tickets: BTreeMap::new(), counter: 0, } } @@ -54,16 +54,16 @@ impl TicketStore { description: ticket.description, status: Status::ToDo, }; - todo!(); + self.tickets.insert(id, ticket); id } pub fn get(&self, id: TicketId) -> Option<&Ticket> { - todo!() + self.tickets.get(&id) } pub fn get_mut(&mut self, id: TicketId) -> Option<&mut Ticket> { - todo!() + self.tickets.get_mut(&id) } } @@ -95,6 +95,15 @@ impl IndexMut<&TicketId> for TicketStore { } } +impl<'a> IntoIterator for &'a TicketStore { + type Item = &'a Ticket; + type IntoIter = std::collections::btree_map::Values<'a, TicketId, Ticket>; + + fn into_iter(self) -> Self::IntoIter { + self.tickets.values() + } +} + #[cfg(test)] mod tests { use crate::{Status, TicketDraft, TicketId, TicketStore}; diff --git a/exercises/07_threads/00_intro/src/lib.rs b/exercises/07_threads/00_intro/src/lib.rs index df6490daf0..283d8d4407 100644 --- a/exercises/07_threads/00_intro/src/lib.rs +++ b/exercises/07_threads/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me πŸ‘‡ - "I'm ready to _!" + "I'm ready to build a concurrent ticket management system!" } #[cfg(test)] diff --git a/exercises/07_threads/01_threads/src/lib.rs b/exercises/07_threads/01_threads/src/lib.rs index 7d084ac1be..b31d0143c3 100644 --- a/exercises/07_threads/01_threads/src/lib.rs +++ b/exercises/07_threads/01_threads/src/lib.rs @@ -15,7 +15,15 @@ use std::thread; pub fn sum(v: Vec) -> i32 { - todo!() + let mid = v.len() / 2; + let (v1, v2) = v.split_at(mid); + let v1 = v1.to_vec(); + let v2 = v2.to_vec(); + + let handle1 = thread::spawn(move || v1.into_iter().sum::()); + let handle2 = thread::spawn(move || v2.into_iter().sum::()); + + handle1.join().unwrap() + handle2.join().unwrap() } #[cfg(test)] diff --git a/exercises/07_threads/02_static/src/lib.rs b/exercises/07_threads/02_static/src/lib.rs index 4b9241b8f3..9011128f58 100644 --- a/exercises/07_threads/02_static/src/lib.rs +++ b/exercises/07_threads/02_static/src/lib.rs @@ -4,7 +4,13 @@ use std::thread; pub fn sum(slice: &'static [i32]) -> i32 { - todo!() + let mid = slice.len() / 2; + let (slice1, slice2) = slice.split_at(mid); + + let handle1 = thread::spawn(move || slice1.iter().sum::()); + let handle2 = thread::spawn(move || slice2.iter().sum::()); + + handle1.join().unwrap() + handle2.join().unwrap() } #[cfg(test)] diff --git a/exercises/07_threads/03_leak/src/lib.rs b/exercises/07_threads/03_leak/src/lib.rs index fef6b46da6..9b7f81f400 100644 --- a/exercises/07_threads/03_leak/src/lib.rs +++ b/exercises/07_threads/03_leak/src/lib.rs @@ -6,7 +6,14 @@ use std::thread; pub fn sum(v: Vec) -> i32 { - todo!() + let v = v.leak(); + let mid = v.len() / 2; + let (v1, v2) = v.split_at(mid); + + let handle1 = thread::spawn(move || v1.into_iter().sum::()); + let handle2 = thread::spawn(move || v2.into_iter().sum::()); + + handle1.join().unwrap() + handle2.join().unwrap() } #[cfg(test)] diff --git a/exercises/07_threads/04_scoped_threads/src/lib.rs b/exercises/07_threads/04_scoped_threads/src/lib.rs index d81a3a9a97..eac1a0ba59 100644 --- a/exercises/07_threads/04_scoped_threads/src/lib.rs +++ b/exercises/07_threads/04_scoped_threads/src/lib.rs @@ -3,7 +3,14 @@ // Don't perform any heap allocation. Don't leak any memory. pub fn sum(v: Vec) -> i32 { - todo!() + let mid = v.len() / 2; + let (left, right) = v.split_at(mid); + + std::thread::scope(|s| { + let left = s.spawn(|| left.iter().sum::()); + let right = s.spawn(|| right.iter().sum::()); + left.join().unwrap() + right.join().unwrap() + }) } #[cfg(test)] diff --git a/exercises/07_threads/05_channels/src/lib.rs b/exercises/07_threads/05_channels/src/lib.rs index 175d1dd145..2e538020ee 100644 --- a/exercises/07_threads/05_channels/src/lib.rs +++ b/exercises/07_threads/05_channels/src/lib.rs @@ -1,10 +1,12 @@ +use crate::data::TicketDraft; +use crate::store::TicketStore; use std::sync::mpsc::{Receiver, Sender}; pub mod data; pub mod store; pub enum Command { - Insert(todo!()), + Insert(TicketDraft), } // Start the system by spawning the server thread. @@ -20,4 +22,13 @@ pub fn launch() -> Sender { // Enter a loop: wait for a command to show up in // the channel, then execute it, then start waiting // for the next command. -pub fn server(receiver: Receiver) {} +pub fn server(receiver: Receiver) { + let mut store = TicketStore::new(); + while let Ok(command) = receiver.recv() { + match command { + Command::Insert(ticket_draft) => { + store.add_ticket(ticket_draft); + } + } + } +} diff --git a/exercises/07_threads/05_channels/tests/insert.rs b/exercises/07_threads/05_channels/tests/insert.rs index 651a467b23..bb72b1aba8 100644 --- a/exercises/07_threads/05_channels/tests/insert.rs +++ b/exercises/07_threads/05_channels/tests/insert.rs @@ -26,7 +26,7 @@ fn ready() { // since our server doesn't expose any **read** actions. // We have no way to know if the inserts are actually happening and if they // are happening correctly. - let move_forward = false; + let move_forward = true; assert!(move_forward); } diff --git a/exercises/07_threads/06_interior_mutability/src/lib.rs b/exercises/07_threads/06_interior_mutability/src/lib.rs index 37d4d4fe32..b937b689a9 100644 --- a/exercises/07_threads/06_interior_mutability/src/lib.rs +++ b/exercises/07_threads/06_interior_mutability/src/lib.rs @@ -6,18 +6,18 @@ use std::rc::Rc; pub struct DropTracker { value: T, - counter: todo!(), + counter: Rc>, } impl DropTracker { - pub fn new(value: T, counter: todo!()) -> Self { + pub fn new(value: T, counter: Rc>) -> Self { Self { value, counter } } } impl Drop for DropTracker { fn drop(&mut self) { - todo!() + *self.counter.borrow_mut() += 1; } } diff --git a/exercises/07_threads/07_ack/src/lib.rs b/exercises/07_threads/07_ack/src/lib.rs index 746c35526d..93bd9e87da 100644 --- a/exercises/07_threads/07_ack/src/lib.rs +++ b/exercises/07_threads/07_ack/src/lib.rs @@ -1,13 +1,20 @@ +use crate::data::{Ticket, TicketDraft}; +use crate::store::{TicketId, TicketStore}; use std::sync::mpsc::{Receiver, Sender}; -use crate::store::TicketStore; pub mod data; pub mod store; // Refer to the tests to understand the expected schema. pub enum Command { - Insert { todo!() }, - Get { todo!() } + Insert { + draft: TicketDraft, + response_sender: Sender, + }, + Get { + id: TicketId, + response_sender: Sender>, + }, } pub fn launch() -> Sender { @@ -21,19 +28,25 @@ pub fn server(receiver: Receiver) { let mut store = TicketStore::new(); loop { match receiver.recv() { - Ok(Command::Insert {}) => { - todo!() + Ok(Command::Insert { + draft, + response_sender, + }) => { + let id = store.add_ticket(draft); + let _ = response_sender.send(id); } Ok(Command::Get { - todo!() + id, + response_sender, }) => { - todo!() + let ticket = store.get(id); + let _ = response_sender.send(ticket.cloned()); } Err(_) => { // There are no more senders, so we can safely break // and shut down the server. - break - }, + break; + } } } } diff --git a/exercises/07_threads/08_client/src/lib.rs b/exercises/07_threads/08_client/src/lib.rs index a934bd308f..edc6f9d687 100644 --- a/exercises/07_threads/08_client/src/lib.rs +++ b/exercises/07_threads/08_client/src/lib.rs @@ -7,23 +7,39 @@ pub mod store; #[derive(Clone)] // TODO: flesh out the client implementation. -pub struct TicketStoreClient {} +pub struct TicketStoreClient { + sender: Sender, +} impl TicketStoreClient { // Feel free to panic on all errors, for simplicity. pub fn insert(&self, draft: TicketDraft) -> TicketId { - todo!() + let (response_sender, response_receiver) = std::sync::mpsc::channel(); + self.sender + .send(Command::Insert { + draft, + response_channel: response_sender, + }) + .unwrap(); + response_receiver.recv().unwrap() } pub fn get(&self, id: TicketId) -> Option { - todo!() + let (response_sender, response_receiver) = std::sync::mpsc::channel(); + self.sender + .send(Command::Get { + id, + response_channel: response_sender, + }) + .unwrap(); + response_receiver.recv().unwrap() } } pub fn launch() -> TicketStoreClient { let (sender, receiver) = std::sync::mpsc::channel(); std::thread::spawn(move || server(receiver)); - todo!() + TicketStoreClient { sender } } // No longer public! This becomes an internal detail of the library now. diff --git a/exercises/07_threads/09_bounded/Cargo.toml b/exercises/07_threads/09_bounded/Cargo.toml index 506d14a29c..b9e755158f 100644 --- a/exercises/07_threads/09_bounded/Cargo.toml +++ b/exercises/07_threads/09_bounded/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] +thiserror = "1.0.60" ticket_fields = { path = "../../../helpers/ticket_fields" } diff --git a/exercises/07_threads/09_bounded/src/lib.rs b/exercises/07_threads/09_bounded/src/lib.rs index e661e68fca..4b0afe1e43 100644 --- a/exercises/07_threads/09_bounded/src/lib.rs +++ b/exercises/07_threads/09_bounded/src/lib.rs @@ -1,44 +1,62 @@ // TODO: Convert the implementation to use bounded channels. use crate::data::{Ticket, TicketDraft}; use crate::store::{TicketId, TicketStore}; -use std::sync::mpsc::{Receiver, Sender}; +use std::sync::mpsc::{Receiver, Sender, SyncSender, TrySendError}; pub mod data; pub mod store; #[derive(Clone)] pub struct TicketStoreClient { - sender: todo!(), + sender: SyncSender, } impl TicketStoreClient { - pub fn insert(&self, draft: TicketDraft) -> Result { - todo!() + pub fn insert(&self, draft: TicketDraft) -> Result { + let (response_sender, response_receiver) = std::sync::mpsc::sync_channel(1); + self.sender + .try_send(Command::Insert { + draft, + response_channel: response_sender, + }) + .map_err(|_| OverloadedError)?; + Ok(response_receiver.recv().unwrap()) } - pub fn get(&self, id: TicketId) -> Result, todo!()> { - todo!() + pub fn get(&self, id: TicketId) -> Result, OverloadedError> { + let (response_sender, response_receiver) = std::sync::mpsc::sync_channel(1); + self.sender + .try_send(Command::Get { + id, + response_channel: response_sender, + }) + .map_err(|_| OverloadedError)?; + Ok(response_receiver.recv().unwrap()) } } +#[derive(Debug, thiserror::Error)] +#[error("The store is overloaded")] +pub struct OverloadedError; + pub fn launch(capacity: usize) -> TicketStoreClient { - todo!(); + let (sender, receiver) = std::sync::mpsc::sync_channel(capacity); std::thread::spawn(move || server(receiver)); - todo!() + TicketStoreClient { sender } } enum Command { Insert { draft: TicketDraft, - response_channel: todo!(), + response_channel: SyncSender, }, Get { id: TicketId, - response_channel: todo!(), + response_channel: SyncSender>, }, } -pub fn server(receiver: Receiver) { +fn server(receiver: Receiver) { let mut store = TicketStore::new(); loop { match receiver.recv() { @@ -47,14 +65,14 @@ pub fn server(receiver: Receiver) { response_channel, }) => { let id = store.add_ticket(draft); - todo!() + let _ = response_channel.send(id); } Ok(Command::Get { id, response_channel, }) => { let ticket = store.get(id); - todo!() + let _ = response_channel.send(ticket.cloned()); } Err(_) => { // There are no more senders, so we can safely break diff --git a/exercises/07_threads/10_patch/src/lib.rs b/exercises/07_threads/10_patch/src/lib.rs index 492f767be9..f0a66d288f 100644 --- a/exercises/07_threads/10_patch/src/lib.rs +++ b/exercises/07_threads/10_patch/src/lib.rs @@ -35,7 +35,16 @@ impl TicketStoreClient { Ok(response_receiver.recv().unwrap()) } - pub fn update(&self, ticket_patch: TicketPatch) -> Result<(), OverloadedError> {} + pub fn update(&self, ticket_patch: TicketPatch) -> Result<(), OverloadedError> { + let (response_sender, response_receiver) = sync_channel(1); + self.sender + .try_send(Command::Update { + patch: ticket_patch, + response_channel: response_sender, + }) + .map_err(|_| OverloadedError)?; + Ok(response_receiver.recv().unwrap()) + } } #[derive(Debug, thiserror::Error)] @@ -85,7 +94,18 @@ pub fn server(receiver: Receiver) { patch, response_channel, }) => { - todo!() + if let Some(ticket) = store.get_mut(patch.id) { + if let Some(title) = patch.title { + ticket.title = title; + } + if let Some(description) = patch.description { + ticket.description = description; + } + if let Some(status) = patch.status { + ticket.status = status; + } + } + let _ = response_channel.send(()); } Err(_) => { // There are no more senders, so we can safely break diff --git a/exercises/07_threads/11_locks/src/store.rs b/exercises/07_threads/11_locks/src/store.rs index 9387499ec0..306e662c27 100644 --- a/exercises/07_threads/11_locks/src/store.rs +++ b/exercises/07_threads/11_locks/src/store.rs @@ -28,13 +28,13 @@ impl TicketStore { description: ticket.description, status: Status::ToDo, }; - todo!(); + self.tickets.insert(id, Arc::new(Mutex::new(ticket))); id } // The `get` method should return a handle to the ticket // which allows the caller to either read or modify the ticket. - pub fn get(&self, id: TicketId) -> Option { - todo!() + pub fn get(&self, id: TicketId) -> Option>> { + self.tickets.get(&id).cloned() } } diff --git a/exercises/07_threads/12_rw_lock/src/lib.rs b/exercises/07_threads/12_rw_lock/src/lib.rs index 0c33f539e5..9164830f5d 100644 --- a/exercises/07_threads/12_rw_lock/src/lib.rs +++ b/exercises/07_threads/12_rw_lock/src/lib.rs @@ -1,7 +1,7 @@ +use std::sync::{Arc, Mutex, RwLock}; // TODO: Replace `Mutex` with `RwLock` in the `TicketStore` struct and // all other relevant places to allow multiple readers to access the ticket store concurrently. -use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError}; -use std::sync::{Arc, Mutex}; +use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; use crate::data::{Ticket, TicketDraft}; use crate::store::{TicketId, TicketStore}; @@ -26,7 +26,7 @@ impl TicketStoreClient { Ok(response_receiver.recv().unwrap()) } - pub fn get(&self, id: TicketId) -> Result>>, OverloadedError> { + pub fn get(&self, id: TicketId) -> Result>>, OverloadedError> { let (response_sender, response_receiver) = sync_channel(1); self.sender .try_send(Command::Get { @@ -55,7 +55,7 @@ enum Command { }, Get { id: TicketId, - response_channel: SyncSender>>>, + response_channel: SyncSender>>>, }, } diff --git a/exercises/07_threads/12_rw_lock/src/store.rs b/exercises/07_threads/12_rw_lock/src/store.rs index 7387efdcb3..c70867c236 100644 --- a/exercises/07_threads/12_rw_lock/src/store.rs +++ b/exercises/07_threads/12_rw_lock/src/store.rs @@ -1,13 +1,13 @@ use crate::data::{Status, Ticket, TicketDraft}; use std::collections::BTreeMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct TicketId(u64); #[derive(Clone)] pub struct TicketStore { - tickets: BTreeMap>>, + tickets: BTreeMap>>, counter: u64, } @@ -28,14 +28,14 @@ impl TicketStore { description: ticket.description, status: Status::ToDo, }; - let ticket = Arc::new(Mutex::new(ticket)); + let ticket = Arc::new(RwLock::new(ticket)); self.tickets.insert(id, ticket); id } // The `get` method should return a handle to the ticket // which allows the caller to either read or modify the ticket. - pub fn get(&self, id: TicketId) -> Option>> { + pub fn get(&self, id: TicketId) -> Option>> { self.tickets.get(&id).cloned() } } diff --git a/exercises/07_threads/13_without_channels/tests/check.rs b/exercises/07_threads/13_without_channels/tests/check.rs index e0d9c88e37..a915761ba2 100644 --- a/exercises/07_threads/13_without_channels/tests/check.rs +++ b/exercises/07_threads/13_without_channels/tests/check.rs @@ -7,7 +7,7 @@ use without_channels::store::TicketStore; #[test] fn works() { - let store = todo!(); + let store = Arc::new(RwLock::new(TicketStore::new())); let store1 = store.clone(); let client1 = spawn(move || { diff --git a/exercises/07_threads/14_sync/src/lib.rs b/exercises/07_threads/14_sync/src/lib.rs index c67d0f7c06..b698498b94 100644 --- a/exercises/07_threads/14_sync/src/lib.rs +++ b/exercises/07_threads/14_sync/src/lib.rs @@ -1,6 +1,6 @@ // Not much to be exercised on `Sync`, just a thing to remember. fn outro() -> &'static str { - "I have a good understanding of __!" + "I have a good understanding of Send and Sync!" } #[cfg(test)] diff --git a/exercises/08_futures/00_intro/src/lib.rs b/exercises/08_futures/00_intro/src/lib.rs index c730220b54..d4644268e2 100644 --- a/exercises/08_futures/00_intro/src/lib.rs +++ b/exercises/08_futures/00_intro/src/lib.rs @@ -1,6 +1,6 @@ fn intro() -> &'static str { // TODO: fix me πŸ‘‡ - "I'm ready to _!" + "I'm ready to learn about futures!" } #[cfg(test)] diff --git a/exercises/08_futures/01_async_fn/src/lib.rs b/exercises/08_futures/01_async_fn/src/lib.rs index b8a83d867c..c9a59f9022 100644 --- a/exercises/08_futures/01_async_fn/src/lib.rs +++ b/exercises/08_futures/01_async_fn/src/lib.rs @@ -11,7 +11,11 @@ use tokio::net::TcpListener; // - `tokio::net::TcpStream::split` to obtain a reader and a writer from the socket // - `tokio::io::copy` to copy data from the reader to the writer pub async fn echo(listener: TcpListener) -> Result<(), anyhow::Error> { - todo!() + loop { + let (mut socket, _) = listener.accept().await?; + let (mut reader, mut writer) = socket.split(); + tokio::io::copy(&mut reader, &mut writer).await?; + } } #[cfg(test)] diff --git a/exercises/08_futures/02_spawn/src/lib.rs b/exercises/08_futures/02_spawn/src/lib.rs index 9e00f3695e..c678ee1c34 100644 --- a/exercises/08_futures/02_spawn/src/lib.rs +++ b/exercises/08_futures/02_spawn/src/lib.rs @@ -4,7 +4,22 @@ use tokio::net::TcpListener; // Multiple connections (on the same listeners) should be processed concurrently. // The received data should be echoed back to the client. pub async fn echoes(first: TcpListener, second: TcpListener) -> Result<(), anyhow::Error> { - todo!() + let handle1 = tokio::spawn(echo(first)); + let handle2 = tokio::spawn(echo(second)); + let (outcome1, outcome2) = tokio::join!(handle1, handle2); + outcome1??; + outcome2??; + Ok(()) +} + +async fn echo(listener: TcpListener) -> Result<(), anyhow::Error> { + loop { + let (mut socket, _) = listener.accept().await?; + tokio::spawn(async move { + let (mut reader, mut writer) = socket.split(); + tokio::io::copy(&mut reader, &mut writer).await.unwrap(); + }); + } } #[cfg(test)] diff --git a/exercises/08_futures/03_runtime/src/lib.rs b/exercises/08_futures/03_runtime/src/lib.rs index 087ddc09a8..89154b8aa2 100644 --- a/exercises/08_futures/03_runtime/src/lib.rs +++ b/exercises/08_futures/03_runtime/src/lib.rs @@ -2,6 +2,7 @@ // accept connections on both of them concurrently, and always reply to clients by sending // the `Display` representation of the `reply` argument as a response. use std::fmt::Display; +use std::sync::Arc; use tokio::io::AsyncWriteExt; use tokio::net::TcpListener; @@ -10,7 +11,25 @@ where // `T` cannot be cloned. How do you share it between the two server tasks? T: Display + Send + Sync + 'static, { - todo!() + let reply = Arc::new(reply); + let handle1 = tokio::spawn(_fixed_reply(first, Arc::clone(&reply))); + let handle2 = tokio::spawn(_fixed_reply(second, reply)); + + tokio::join!(handle1, handle2,); +} + +async fn _fixed_reply(listener: TcpListener, reply: Arc) +where + T: Display + Send + Sync + 'static, +{ + loop { + let (mut socket, _) = listener.accept().await.unwrap(); + let (_reader, mut writer) = socket.split(); + writer + .write_all(format!("{}", reply).as_bytes()) + .await + .unwrap(); + } } #[cfg(test)] diff --git a/exercises/08_futures/04_future/src/lib.rs b/exercises/08_futures/04_future/src/lib.rs index 8dc1c962d0..4980f8d621 100644 --- a/exercises/08_futures/04_future/src/lib.rs +++ b/exercises/08_futures/04_future/src/lib.rs @@ -10,7 +10,9 @@ fn spawner() { } async fn example() { - let non_send = Rc::new(1); + { + let non_send = Rc::new(1); + println!("{}", non_send); + } yield_now().await; - println!("{}", non_send); } diff --git a/exercises/08_futures/05_blocking/src/lib.rs b/exercises/08_futures/05_blocking/src/lib.rs index e9b4354770..34538cf6f7 100644 --- a/exercises/08_futures/05_blocking/src/lib.rs +++ b/exercises/08_futures/05_blocking/src/lib.rs @@ -4,15 +4,19 @@ // Use `spawn_blocking` inside `echo` to resolve the issue. use std::io::{Read, Write}; use tokio::net::TcpListener; +use tokio::task::spawn_blocking; pub async fn echo(listener: TcpListener) -> Result<(), anyhow::Error> { loop { let (socket, _) = listener.accept().await?; let mut socket = socket.into_std()?; - socket.set_nonblocking(false)?; - let mut buffer = Vec::new(); - socket.read_to_end(&mut buffer)?; - socket.write_all(&buffer)?; + spawn_blocking(move || -> Result<(), anyhow::Error> { + socket.set_nonblocking(false)?; + let mut buffer = Vec::new(); + socket.read_to_end(&mut buffer)?; + socket.write_all(&buffer)?; + Ok(()) + }); } } diff --git a/exercises/08_futures/06_async_aware_primitives/src/lib.rs b/exercises/08_futures/06_async_aware_primitives/src/lib.rs index 16efb4a72e..fe62b6bdac 100644 --- a/exercises/08_futures/06_async_aware_primitives/src/lib.rs +++ b/exercises/08_futures/06_async_aware_primitives/src/lib.rs @@ -4,7 +4,7 @@ /// the testing code too, yes). /// /// Can you understand the sequence of events that can lead to a deadlock? -use std::sync::mpsc; +use tokio::sync::mpsc; pub struct Message { payload: String, @@ -15,14 +15,15 @@ pub struct Message { /// channel to continue communicating with the caller. pub async fn pong(mut receiver: mpsc::Receiver) { loop { - if let Ok(msg) = receiver.recv() { + if let Some(msg) = receiver.recv().await { println!("Pong received: {}", msg.payload); - let (sender, new_receiver) = mpsc::channel(); + let (sender, new_receiver) = mpsc::channel(1); msg.response_channel .send(Message { payload: "pong".into(), response_channel: sender, }) + .await .unwrap(); receiver = new_receiver; } @@ -32,22 +33,23 @@ pub async fn pong(mut receiver: mpsc::Receiver) { #[cfg(test)] mod tests { use crate::{pong, Message}; - use std::sync::mpsc; + use tokio::sync::mpsc; #[tokio::test] async fn ping() { - let (sender, receiver) = mpsc::channel(); - let (response_sender, response_receiver) = mpsc::channel(); + let (sender, receiver) = mpsc::channel(1); + let (response_sender, mut response_receiver) = mpsc::channel(1); sender .send(Message { payload: "pong".into(), response_channel: response_sender, }) + .await .unwrap(); tokio::spawn(pong(receiver)); - let answer = response_receiver.recv().unwrap().payload; + let answer = response_receiver.recv().await.unwrap().payload; assert_eq!(answer, "pong"); } } diff --git a/exercises/08_futures/07_cancellation/src/lib.rs b/exercises/08_futures/07_cancellation/src/lib.rs index f62301e941..30c9c2f9a8 100644 --- a/exercises/08_futures/07_cancellation/src/lib.rs +++ b/exercises/08_futures/07_cancellation/src/lib.rs @@ -46,6 +46,6 @@ mod tests { let buffered = handle.await.unwrap(); let buffered = std::str::from_utf8(&buffered).unwrap(); - assert_eq!(buffered, ""); + assert_eq!(buffered, "hefrthta"); } } From 486096e94e1d40c5892005a187c75f552b59666f Mon Sep 17 00:00:00 2001 From: LukeMathWalker <20745048+LukeMathWalker@users.noreply.github.com> Date: Wed, 22 Jan 2025 11:10:24 +0100 Subject: [PATCH 2/4] chore: Link to wr's website to avoid installing from source --- book/src/01_intro/00_welcome.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/book/src/01_intro/00_welcome.md b/book/src/01_intro/00_welcome.md index 33da833ba5..8986900681 100644 --- a/book/src/01_intro/00_welcome.md +++ b/book/src/01_intro/00_welcome.md @@ -78,15 +78,10 @@ To work through this course, you'll need: - [RustRover](https://www.jetbrains.com/rust/); - [Visual Studio Code](https://code.visualstudio.com) with the [`rust-analyzer`](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer) extension. -To verify your solutions, we've also provided a tool to guide you through the course. -It is the `wr` CLI (short for "workshop runner"). -Install it with: +To verify your solutions, we've also provided a tool to guide you through the course: the `wr` CLI, short for "workshop runner". +Install `wr` by following the instructions on [its website](https://mainmatter.github.io/rust-workshop-runner/). -```bash -cargo install --locked workshop-runner -``` - -In a new terminal, navigate back to the top-level folder of the repository. +Once you have `wr` installed, open a new terminal and navigate to the top-level folder of the repository. Run the `wr` command to start the course: ```bash From 04c1f3b38a5c1e07603b2fc5e615f119bbe214c6 Mon Sep 17 00:00:00 2001 From: MohammadHosein Masoon Date: Mon, 27 Jan 2025 16:17:35 +0330 Subject: [PATCH 3/4] match panic message with the test expected message --- exercises/02_basic_calculator/04_panics/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/02_basic_calculator/04_panics/src/lib.rs b/exercises/02_basic_calculator/04_panics/src/lib.rs index f70452fa18..b61b2cf645 100644 --- a/exercises/02_basic_calculator/04_panics/src/lib.rs +++ b/exercises/02_basic_calculator/04_panics/src/lib.rs @@ -3,7 +3,7 @@ fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 { // TODO: Panic with a custom message if `time_elapsed` is 0 if time_elapsed == 0 { - panic!("The journey took no time at all, that's impossible!"); + panic!("The journey took no time at all. That's impossible!"); } (end - start) / time_elapsed From f5a4df96f9d44f061e94e0cea851615dfbf9c8c0 Mon Sep 17 00:00:00 2001 From: panpilkarz Date: Sun, 7 Sep 2025 11:21:38 +0200 Subject: [PATCH 4/4] Remove uneccessary move --- exercises/07_threads/01_threads/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/07_threads/01_threads/src/lib.rs b/exercises/07_threads/01_threads/src/lib.rs index b31d0143c3..15bb7d1355 100644 --- a/exercises/07_threads/01_threads/src/lib.rs +++ b/exercises/07_threads/01_threads/src/lib.rs @@ -20,8 +20,8 @@ pub fn sum(v: Vec) -> i32 { let v1 = v1.to_vec(); let v2 = v2.to_vec(); - let handle1 = thread::spawn(move || v1.into_iter().sum::()); - let handle2 = thread::spawn(move || v2.into_iter().sum::()); + let handle1 = thread::spawn(|| v1.into_iter().sum::()); + let handle2 = thread::spawn(|| v2.into_iter().sum::()); handle1.join().unwrap() + handle2.join().unwrap() }