diff options
| author | mo8it <mo8it@proton.me> | 2024-07-05 13:39:50 +0200 |
|---|---|---|
| committer | mo8it <mo8it@proton.me> | 2024-07-05 13:39:50 +0200 |
| commit | 7123c7ae3a9605fbe962e4ef0a0f1424cd16fef8 (patch) | |
| tree | c67f7e62bb9a179ae4fdbab492501cb6847e64c7 /exercises | |
| parent | 77b687d501771c24bd83294d97b8e6f9ffa92d6b (diff) | |
| parent | 4d9c346a173bb722b929f3ea3c00f84954483e24 (diff) | |
Merge remote-tracking branch 'upstream/main' into fix-enum-variant-inconsistency
Diffstat (limited to 'exercises')
102 files changed, 1440 insertions, 1914 deletions
diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 5dd18b4..7b8baa2 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,19 +1,9 @@ -// intro1.rs +// TODO: We sometimes encourage you to keep trying things on a given exercise, +// even after you already figured it out. If you got everything working and feel +// ready for the next exercise, enter `n` in the terminal. // -// About this `I AM NOT DONE` thing: -// We sometimes encourage you to keep trying things on a given exercise, even -// after you already figured it out. If you got everything working and feel -// ready for the next exercise, remove the `I AM NOT DONE` comment below. -// -// If you're running this using `rustlings watch`: The exercise file will be -// reloaded when you change one of the lines below! Try adding a `println!` -// line, or try changing what it outputs in your terminal. Try removing a -// semicolon and see what happens! -// -// Execute `rustlings hint intro1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// The exercise file will be reloaded when you change one of the lines below! +// Try adding a new `println!` and check the updated output in the terminal. fn main() { println!("Hello and"); @@ -29,13 +19,7 @@ fn main() { println!("or logic error. The central concept behind Rustlings is to fix these errors and"); println!("solve the exercises. Good luck!"); println!(); - println!("The source for this exercise is in `exercises/00_intro/intro1.rs`. Have a look!"); - println!( - "Going forward, the source of the exercises will always be in the success/failure output." - ); - println!(); - println!( - "If you want to use rust-analyzer, Rust's LSP implementation, make sure your editor is set" - ); - println!("up, and then run `rustlings lsp` before continuing.") + println!("The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!"); + println!("The current exercise path will be always shown under the progress bar."); + println!("You can click on the path to open the exercise file in your editor."); } diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index a28ad3d..c6cb645 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -1,12 +1,4 @@ -// intro2.rs -// -// Make the code print a greeting to the world. -// -// Execute `rustlings hint intro2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - fn main() { - printline!("Hello there!") + // TODO: Fix the code to print "Hello world!". + printline!("Hello world!"); } diff --git a/exercises/01_variables/variables1.rs b/exercises/01_variables/variables1.rs index b3e089a..0a9e554 100644 --- a/exercises/01_variables/variables1.rs +++ b/exercises/01_variables/variables1.rs @@ -1,13 +1,6 @@ -// variables1.rs -// -// Make me compile! -// -// Execute `rustlings hint variables1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - fn main() { + // TODO: Add missing keyword. x = 5; - println!("x has the value {}", x); + + println!("x has the value {x}"); } diff --git a/exercises/01_variables/variables2.rs b/exercises/01_variables/variables2.rs index e1c23ed..e2a3603 100644 --- a/exercises/01_variables/variables2.rs +++ b/exercises/01_variables/variables2.rs @@ -1,12 +1,7 @@ -// variables2.rs -// -// Execute `rustlings hint variables2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - fn main() { + // TODO: Change the line below to fix the compiler error. let x; + if x == 10 { println!("x is ten!"); } else { diff --git a/exercises/01_variables/variables3.rs b/exercises/01_variables/variables3.rs index 86bed41..06f35bb 100644 --- a/exercises/01_variables/variables3.rs +++ b/exercises/01_variables/variables3.rs @@ -1,11 +1,6 @@ -// variables3.rs -// -// Execute `rustlings hint variables3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - fn main() { + // TODO: Change the line below to fix the compiler error. let x: i32; - println!("Number {}", x); + + println!("Number {x}"); } diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs index 5394f39..6c138b1 100644 --- a/exercises/01_variables/variables4.rs +++ b/exercises/01_variables/variables4.rs @@ -1,13 +1,8 @@ -// variables4.rs -// -// Execute `rustlings hint variables4` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +// TODO: Fix the compiler error. fn main() { let x = 3; - println!("Number {}", x); - x = 5; // don't change this line - println!("Number {}", x); + println!("Number {x}"); + + x = 5; // Don't change this line + println!("Number {x}"); } diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs index a29b38b..49db8e9 100644 --- a/exercises/01_variables/variables5.rs +++ b/exercises/01_variables/variables5.rs @@ -1,13 +1,8 @@ -// variables5.rs -// -// Execute `rustlings hint variables5` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - fn main() { - let number = "T-H-R-E-E"; // don't change this line - println!("Spell a Number : {}", number); - number = 3; // don't rename this variable - println!("Number plus two is : {}", number + 2); + let number = "T-H-R-E-E"; // Don't change this line + println!("Spell a number: {}", number); + + // TODO: Fix the compiler error by changing the line below without renaming the variable. + number = 3; + println!("Number plus two is: {}", number + 2); } diff --git a/exercises/01_variables/variables6.rs b/exercises/01_variables/variables6.rs index 853183b..4a040fd 100644 --- a/exercises/01_variables/variables6.rs +++ b/exercises/01_variables/variables6.rs @@ -1,11 +1,6 @@ -// variables6.rs -// -// Execute `rustlings hint variables6` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +// TODO: Change the line below to fix the compiler error. const NUMBER = 3; + fn main() { - println!("Number {}", NUMBER); + println!("Number: {NUMBER}"); } diff --git a/exercises/02_functions/functions1.rs b/exercises/02_functions/functions1.rs index 40ed9a0..a812c21 100644 --- a/exercises/02_functions/functions1.rs +++ b/exercises/02_functions/functions1.rs @@ -1,10 +1,5 @@ -// functions1.rs -// -// Execute `rustlings hint functions1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// TODO: Add some function with the name `call_me` without arguments or a return value. fn main() { - call_me(); + call_me(); // Don't change this line } diff --git a/exercises/02_functions/functions2.rs b/exercises/02_functions/functions2.rs index 5154f34..2c773c6 100644 --- a/exercises/02_functions/functions2.rs +++ b/exercises/02_functions/functions2.rs @@ -1,16 +1,10 @@ -// functions2.rs -// -// Execute `rustlings hint functions2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -fn main() { - call_me(3); -} - +// TODO: Add the missing type of the argument `num` after the colon `:`. fn call_me(num:) { for i in 0..num { println!("Ring! Call number {}", i + 1); } } + +fn main() { + call_me(3); +} diff --git a/exercises/02_functions/functions3.rs b/exercises/02_functions/functions3.rs index 74f44d6..5d5122a 100644 --- a/exercises/02_functions/functions3.rs +++ b/exercises/02_functions/functions3.rs @@ -1,16 +1,10 @@ -// functions3.rs -// -// Execute `rustlings hint functions3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -fn main() { - call_me(); -} - fn call_me(num: u32) { for i in 0..num { println!("Ring! Call number {}", i + 1); } } + +fn main() { + // TODO: Fix the function call. + call_me(); +} diff --git a/exercises/02_functions/functions4.rs b/exercises/02_functions/functions4.rs index 77c4b2a..b22bffd 100644 --- a/exercises/02_functions/functions4.rs +++ b/exercises/02_functions/functions4.rs @@ -1,21 +1,14 @@ -// functions4.rs -// // This store is having a sale where if the price is an even number, you get 10 -// Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. (Don't worry -// about the function bodies themselves, we're only interested in the signatures -// for now. If anything, this is a good way to peek ahead to future exercises!) -// -// Execute `rustlings hint functions4` or use the `hint` watch subcommand for a -// hint. +// Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. +// Don't worry about the function bodies themselves, we are only interested in +// the signatures for now. -// I AM NOT DONE - -fn main() { - let original_price = 51; - println!("Your sale price is {}", sale_price(original_price)); +fn is_even(num: i64) -> bool { + num % 2 == 0 } -fn sale_price(price: i32) -> { +// TODO: Fix the function signature. +fn sale_price(price: i64) -> { if is_even(price) { price - 10 } else { @@ -23,6 +16,7 @@ fn sale_price(price: i32) -> { } } -fn is_even(num: i32) -> bool { - num % 2 == 0 +fn main() { + let original_price = 51; + println!("Your sale price is {}", sale_price(original_price)); } diff --git a/exercises/02_functions/functions5.rs b/exercises/02_functions/functions5.rs index f1b63f4..34a2ac7 100644 --- a/exercises/02_functions/functions5.rs +++ b/exercises/02_functions/functions5.rs @@ -1,15 +1,9 @@ -// functions5.rs -// -// Execute `rustlings hint functions5` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// TODO: Fix the function body without changing the signature. +fn square(num: i32) -> i32 { + num * num; +} fn main() { let answer = square(3); - println!("The square of 3 is {}", answer); -} - -fn square(num: i32) -> i32 { - num * num; + println!("The square of 3 is {answer}"); } diff --git a/exercises/03_if/if1.rs b/exercises/03_if/if1.rs index 4734d78..e5a3c5a 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -1,16 +1,15 @@ -// if1.rs -// -// Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE - -pub fn bigger(a: i32, b: i32) -> i32 { - // Complete this function to return the bigger number! +fn bigger(a: i32, b: i32) -> i32 { + // TODO: Complete this function to return the bigger number! + // If both numbers are equal, any of them can be returned. // Do not use: // - another function call // - additional variables } +fn main() { + // You can optionally experiment here. +} + // Don't mind this for now :) #[cfg(test)] mod tests { diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index f512f13..593a77a 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -1,13 +1,5 @@ -// if2.rs -// -// Step 1: Make me compile! -// Step 2: Get the bar_for_fuzz and default_to_baz tests passing! -// -// Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE - -pub fn foo_if_fizz(fizzish: &str) -> &str { +// TODO: Fix the compiler error on this function. +fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" } else { @@ -15,23 +7,29 @@ pub fn foo_if_fizz(fizzish: &str) -> &str { } } -// No test changes needed! +fn main() { + // You can optionally experiment here. +} + +// TODO: Read the tests to understand the desired behavior. +// Make all tests pass without changing them. #[cfg(test)] mod tests { use super::*; #[test] fn foo_for_fizz() { - assert_eq!(foo_if_fizz("fizz"), "foo") + // This means that calling `foo_if_fizz` with the argument "fizz" should return "foo". + assert_eq!(foo_if_fizz("fizz"), "foo"); } #[test] fn bar_for_fuzz() { - assert_eq!(foo_if_fizz("fuzz"), "bar") + assert_eq!(foo_if_fizz("fuzz"), "bar"); } #[test] fn default_to_baz() { - assert_eq!(foo_if_fizz("literally anything"), "baz") + assert_eq!(foo_if_fizz("literally anything"), "baz"); } } diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs index 1696274..89164eb 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -1,10 +1,5 @@ -// if3.rs -// -// Execute `rustlings hint if3` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE - -pub fn animal_habitat(animal: &str) -> &'static str { +fn animal_habitat(animal: &str) -> &str { + // TODO: Fix the compiler error in the statement below. let identifier = if animal == "crab" { 1 } else if animal == "gopher" { @@ -15,8 +10,8 @@ pub fn animal_habitat(animal: &str) -> &'static str { "Unknown" }; - // DO NOT CHANGE THIS STATEMENT BELOW - let habitat = if identifier == 1 { + // Don't change the expression below! + if identifier == 1 { "Beach" } else if identifier == 2 { "Burrow" @@ -24,12 +19,14 @@ pub fn animal_habitat(animal: &str) -> &'static str { "Desert" } else { "Unknown" - }; + } +} - habitat +fn main() { + // You can optionally experiment here. } -// No test changes needed. +// Don't change the tests! #[cfg(test)] mod tests { use super::*; diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs index 3663340..84923c7 100644 --- a/exercises/04_primitive_types/primitive_types1.rs +++ b/exercises/04_primitive_types/primitive_types1.rs @@ -1,19 +1,14 @@ -// primitive_types1.rs -// -// Fill in the rest of the line that has code missing! No hints, there's no -// tricks, just get used to typing these :) - -// I AM NOT DONE +// Booleans (`bool`) fn main() { - // Booleans (`bool`) - let is_morning = true; if is_morning { println!("Good morning!"); } - let // Finish the rest of this line like the example! Or make it be false! + // TODO: Define a boolean variable with the name `is_evening` before the `if` statement below. + // The value of the variable should be the negation (opposite) of `is_morning`. + // let โฆ if is_evening { println!("Good evening!"); } diff --git a/exercises/04_primitive_types/primitive_types2.rs b/exercises/04_primitive_types/primitive_types2.rs index f1616ed..1401847 100644 --- a/exercises/04_primitive_types/primitive_types2.rs +++ b/exercises/04_primitive_types/primitive_types2.rs @@ -1,13 +1,6 @@ -// primitive_types2.rs -// -// Fill in the rest of the line that has code missing! No hints, there's no -// tricks, just get used to typing these :) - -// I AM NOT DONE +// Characters (`char`) fn main() { - // Characters (`char`) - // Note the _single_ quotes, these are different from the double quotes // you've been seeing around. let my_first_initial = 'C'; @@ -19,9 +12,12 @@ fn main() { println!("Neither alphabetic nor numeric!"); } - let // Finish this line like the example! What's your favorite character? - // Try a letter, try a number, try a special character, try a character - // from a different language than your own, try an emoji! + // TODO: Analogous to the example before, declare a variable called `your_character` + // below with your favorite character. + // Try a letter, try a digit (in single quotes), try a special character, try a character + // from a different language than your own, try an emoji ๐ + // let your_character = ''; + if your_character.is_alphabetic() { println!("Alphabetical!"); } else if your_character.is_numeric() { diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs index 8b0de44..9b79c0c 100644 --- a/exercises/04_primitive_types/primitive_types3.rs +++ b/exercises/04_primitive_types/primitive_types3.rs @@ -1,19 +1,11 @@ -// primitive_types3.rs -// -// Create an array with at least 100 elements in it where the ??? is. -// -// Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand -// for a hint. - -// I AM NOT DONE - fn main() { - let a = ??? + // TODO: Create an array called `a` with at least 100 elements in it. + // let a = ??? if a.len() >= 100 { println!("Wow, that's a big array!"); } else { println!("Meh, I eat arrays like that for breakfast."); - panic!("Array not big enough, more elements needed") + panic!("Array not big enough, more elements needed"); } } diff --git a/exercises/04_primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs index d44d877..16e4fd9 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -1,17 +1,16 @@ -// primitive_types4.rs -// -// Get a slice out of Array a where the ??? is so that the test passes. -// -// Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand -// for a hint. - -// I AM NOT DONE +fn main() { + // You can optionally experiment here. +} -#[test] -fn slice_out_of_array() { - let a = [1, 2, 3, 4, 5]; +#[cfg(test)] +mod tests { + #[test] + fn slice_out_of_array() { + let a = [1, 2, 3, 4, 5]; - let nice_slice = ??? + // TODO: Get a slice called `nice_slice` out of the array `a` so that the test passes. + // let nice_slice = ??? - assert_eq!([2, 3, 4], nice_slice) + assert_eq!([2, 3, 4], nice_slice); + } } diff --git a/exercises/04_primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs index f646986..6e00ef5 100644 --- a/exercises/04_primitive_types/primitive_types5.rs +++ b/exercises/04_primitive_types/primitive_types5.rs @@ -1,15 +1,8 @@ -// primitive_types5.rs -// -// Destructure the `cat` tuple so that the println will work. -// -// Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand -// for a hint. - -// I AM NOT DONE - fn main() { let cat = ("Furry McFurson", 3.5); - let /* your pattern here */ = cat; - println!("{} is {} years old.", name, age); + // TODO: Destructure the `cat` tuple in one statement so that the println works. + // let /* your pattern here */ = cat; + + println!("{name} is {age} years old"); } diff --git a/exercises/04_primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs index 07cc46c..a97e531 100644 --- a/exercises/04_primitive_types/primitive_types6.rs +++ b/exercises/04_primitive_types/primitive_types6.rs @@ -1,19 +1,17 @@ -// primitive_types6.rs -// -// Use a tuple index to access the second element of `numbers`. You can put the -// expression for the second element where ??? is so that the test passes. -// -// Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand -// for a hint. +fn main() { + // You can optionally experiment here. +} -// I AM NOT DONE +#[cfg(test)] +mod tests { + #[test] + fn indexing_tuple() { + let numbers = (1, 2, 3); -#[test] -fn indexing_tuple() { - let numbers = (1, 2, 3); - // Replace below ??? with the tuple indexing syntax. - let second = ???; + // TODO: Use a tuple index to access the second element of `numbers` + // and assign it to a variable called `second`. + // let second = ???; - assert_eq!(2, second, - "This is not the 2nd number in the tuple!") + assert_eq!(second, 2, "This is not the 2nd number in the tuple!"); + } } diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs index 65b7a7f..68e1aff 100644 --- a/exercises/05_vecs/vecs1.rs +++ b/exercises/05_vecs/vecs1.rs @@ -1,21 +1,17 @@ -// vecs1.rs -// -// Your task is to create a `Vec` which holds the exact same elements as in the -// array `a`. -// -// Make me compile and pass the test! -// -// Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE - fn array_and_vec() -> ([i32; 4], Vec<i32>) { - let a = [10, 20, 30, 40]; // a plain array - let v = // TODO: declare your vector here with the macro for vectors + let a = [10, 20, 30, 40]; // Array + + // TODO: Create a vector called `v` which contains the exact same elements as in the array `a`. + // Use the vector macro. + // let v = ???; (a, v) } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -23,6 +19,6 @@ mod tests { #[test] fn test_array_and_vec_similarity() { let (a, v) = array_and_vec(); - assert_eq!(a, v[..]); + assert_eq!(a, *v); } } diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs index e92c970..a9be258 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -1,31 +1,36 @@ -// vecs2.rs -// -// A Vec of even numbers is given. Your task is to complete the loop so that -// each number in the Vec is multiplied by 2. -// -// Make me pass the test! -// -// Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE - -fn vec_loop(mut v: Vec<i32>) -> Vec<i32> { - for element in v.iter_mut() { - // TODO: Fill this up so that each element in the Vec `v` is - // multiplied by 2. - ??? +fn vec_loop(input: &[i32]) -> Vec<i32> { + let mut output = Vec::new(); + + for element in input { + // TODO: Multiply each element in the `input` slice by 2 and push it to + // the `output` vector. } - // At this point, `v` should be equal to [4, 8, 12, 16, 20]. - v + output +} + +fn vec_map_example(input: &[i32]) -> Vec<i32> { + // An example of collecting a vector after mapping. + // We map each element of the `input` slice to its value plus 1. + // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. + input.iter().map(|element| element + 1).collect() +} + +fn vec_map(input: &[i32]) -> Vec<i32> { + // TODO: Here, we also want to multiply each element in the `input` slice + // by 2, but with iterator mapping instead of manually pushing into an empty + // vector. + // See the example in the function `vec_map_example` above. + input + .iter() + .map(|element| { + // ??? + }) + .collect() } -fn vec_map(v: &Vec<i32>) -> Vec<i32> { - v.iter().map(|element| { - // TODO: Do the same thing as above - but instead of mutating the - // Vec, you can just return the new number! - ??? - }).collect() +fn main() { + // You can optionally experiment here. } #[cfg(test)] @@ -34,17 +39,22 @@ mod tests { #[test] fn test_vec_loop() { - let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect(); - let ans = vec_loop(v.clone()); + let input = [2, 4, 6, 8, 10]; + let ans = vec_loop(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); + } - assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>()); + #[test] + fn test_vec_map_example() { + let input = [1, 2, 3]; + let ans = vec_map_example(&input); + assert_eq!(ans, [2, 3, 4]); } #[test] fn test_vec_map() { - let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect(); - let ans = vec_map(&v); - - assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>()); + let input = [2, 4, 6, 8, 10]; + let ans = vec_map(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); } } diff --git a/exercises/06_move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs index e063937..4eb3d61 100644 --- a/exercises/06_move_semantics/move_semantics1.rs +++ b/exercises/06_move_semantics/move_semantics1.rs @@ -1,19 +1,4 @@ -// move_semantics1.rs -// -// Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand -// for a hint. - -// I AM NOT DONE - -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; - - let vec1 = fill_vec(vec0); - - assert_eq!(vec1, vec![22, 44, 66, 88]); -} - +// TODO: Fix the compiler error in this function. fn fill_vec(vec: Vec<i32>) -> Vec<i32> { let vec = vec; @@ -21,3 +6,19 @@ fn fill_vec(vec: Vec<i32>) -> Vec<i32> { vec } + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics1() { + let vec0 = vec![22, 44, 66]; + let vec1 = fill_vec(vec0); + assert_eq!(vec1, vec![22, 44, 66, 88]); + } +} diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs index baf6bcc..a3ab7a0 100644 --- a/exercises/06_move_semantics/move_semantics2.rs +++ b/exercises/06_move_semantics/move_semantics2.rs @@ -1,26 +1,28 @@ -// move_semantics2.rs -// -// Make the test pass by finding a way to keep both Vecs separate! -// -// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand -// for a hint. - -// I AM NOT DONE +fn fill_vec(vec: Vec<i32>) -> Vec<i32> { + let mut vec = vec; -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; + vec.push(88); - let mut vec1 = fill_vec(vec0); + vec +} - assert_eq!(vec0, vec![22, 44, 66]); - assert_eq!(vec1, vec![22, 44, 66, 88]); +fn main() { + // You can optionally experiment here. } -fn fill_vec(vec: Vec<i32>) -> Vec<i32> { - let mut vec = vec; +#[cfg(test)] +mod tests { + use super::*; - vec.push(88); + // TODO: Make both vectors `vec0` and `vec1` accessible at the same time to + // fix the compiler error in the test. + #[test] + fn move_semantics2() { + let vec0 = vec![22, 44, 66]; - vec + let vec1 = fill_vec(vec0); + + assert_eq!(vec0, [22, 44, 66]); + assert_eq!(vec1, [22, 44, 66, 88]); + } } diff --git a/exercises/06_move_semantics/move_semantics3.rs b/exercises/06_move_semantics/move_semantics3.rs index 7af9e69..11dbbbe 100644 --- a/exercises/06_move_semantics/move_semantics3.rs +++ b/exercises/06_move_semantics/move_semantics3.rs @@ -1,24 +1,22 @@ -// move_semantics3.rs -// -// Make me compile without adding new lines -- just changing existing lines! (no -// lines with multiple semicolons necessary!) -// -// Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand -// for a hint. +// TODO: Fix the compiler error in the function without adding any new line. +fn fill_vec(vec: Vec<i32>) -> Vec<i32> { + vec.push(88); -// I AM NOT DONE + vec +} -#[test] fn main() { - let vec0 = vec![22, 44, 66]; - - let mut vec1 = fill_vec(vec0); - - assert_eq!(vec1, vec![22, 44, 66, 88]); + // You can optionally experiment here. } -fn fill_vec(vec: Vec<i32>) -> Vec<i32> { - vec.push(88); +#[cfg(test)] +mod tests { + use super::*; - vec + #[test] + fn move_semantics3() { + let vec0 = vec![22, 44, 66]; + let vec1 = fill_vec(vec0); + assert_eq!(vec1, [22, 44, 66, 88]); + } } diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index 80b49db..83a0344 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -1,29 +1,18 @@ -// move_semantics4.rs -// -// Refactor this code so that instead of passing `vec0` into the `fill_vec` -// function, the Vector gets created in the function itself and passed back to -// the main function. -// -// Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand -// for a hint. - -// I AM NOT DONE - -#[test] fn main() { - let vec0 = vec![22, 44, 66]; - - let mut vec1 = fill_vec(vec0); - - assert_eq!(vec1, vec![22, 44, 66, 88]); + // You can optionally experiment here. } -// `fill_vec()` no longer takes `vec: Vec<i32>` as argument - don't change this! -fn fill_vec() -> Vec<i32> { - // Instead, let's create and fill the Vec in here - how do you do that? - let mut vec = vec; - - vec.push(88); - - vec +#[cfg(test)] +mod tests { + // TODO: Fix the compiler errors only by reordering the lines in the test. + // Don't add, change or remove any line. + #[test] + fn move_semantics4() { + let mut x = 100; + let y = &mut x; + let z = &mut x; + *y += 100; + *z += 1000; + assert_eq!(x, 1200); + } } diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index 267bdcc..fc59338 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -1,19 +1,24 @@ -// move_semantics5.rs -// -// Make me compile only by reordering the lines in `main()`, but without adding, -// changing or removing any of them. -// -// Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand -// for a hint. +#![allow(clippy::ptr_arg)] -// I AM NOT DONE +// TODO: Fix the compiler errors without changing anything except adding or +// removing references (the character `&`). -#[test] fn main() { - let mut x = 100; - let y = &mut x; - let z = &mut x; - *y += 100; - *z += 1000; - assert_eq!(x, 1200); + let data = "Rust is great!".to_string(); + + get_char(data); + + string_uppercase(&data); +} + +// Shouldn't take ownership +fn get_char(data: String) -> char { + data.chars().last().unwrap() +} + +// Should take ownership +fn string_uppercase(mut data: &String) { + data = data.to_uppercase(); + + println!("{data}"); } diff --git a/exercises/06_move_semantics/move_semantics6.rs b/exercises/06_move_semantics/move_semantics6.rs deleted file mode 100644 index cace4ca..0000000 --- a/exercises/06_move_semantics/move_semantics6.rs +++ /dev/null @@ -1,28 +0,0 @@ -// move_semantics6.rs -// -// You can't change anything except adding or removing references. -// -// Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand -// for a hint. - -// I AM NOT DONE - -fn main() { - let data = "Rust is great!".to_string(); - - get_char(data); - - string_uppercase(&data); -} - -// Should not take ownership -fn get_char(data: String) -> char { - data.chars().last().unwrap() -} - -// Should take ownership -fn string_uppercase(mut data: &String) { - data = &data.to_uppercase(); - - println!("{}", data); -} diff --git a/exercises/07_structs/structs1.rs b/exercises/07_structs/structs1.rs index 5fa5821..959c4c6 100644 --- a/exercises/07_structs/structs1.rs +++ b/exercises/07_structs/structs1.rs @@ -1,28 +1,24 @@ -// structs1.rs -// -// Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint structs1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -struct ColorClassicStruct { - // TODO: Something goes here +struct ColorRegularStruct { + // TODO: Add the fields that the test `regular_structs` expects. + // What types should the fields have? What are the minimum and maximum values for RGB colors? } -struct ColorTupleStruct(/* TODO: Something goes here */); +struct ColorTupleStruct(/* TODO: Add the fields that the test `tuple_structs` expects */); #[derive(Debug)] -struct UnitLikeStruct; +struct UnitStruct; + +fn main() { + // You can optionally experiment here. +} #[cfg(test)] mod tests { use super::*; #[test] - fn classic_c_structs() { - // TODO: Instantiate a classic c struct! + fn regular_structs() { + // TODO: Instantiate a regular struct. // let green = assert_eq!(green.red, 0); @@ -32,7 +28,7 @@ mod tests { #[test] fn tuple_structs() { - // TODO: Instantiate a tuple struct! + // TODO: Instantiate a tuple struct. // let green = assert_eq!(green.0, 0); @@ -42,10 +38,10 @@ mod tests { #[test] fn unit_structs() { - // TODO: Instantiate a unit-like struct! - // let unit_like_struct = - let message = format!("{:?}s are fun!", unit_like_struct); + // TODO: Instantiate a unit struct. + // let unit_struct = + let message = format!("{unit_struct:?}s are fun!"); - assert_eq!(message, "UnitLikeStructs are fun!"); + assert_eq!(message, "UnitStructs are fun!"); } } diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs index 328567f..79141af 100644 --- a/exercises/07_structs/structs2.rs +++ b/exercises/07_structs/structs2.rs @@ -1,12 +1,3 @@ -// structs2.rs -// -// Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - #[derive(Debug)] struct Order { name: String, @@ -30,6 +21,10 @@ fn create_order_template() -> Order { } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -37,8 +32,10 @@ mod tests { #[test] fn your_order() { let order_template = create_order_template(); + // TODO: Create your own order using the update syntax and template above! // let your_order = + assert_eq!(your_order.name, "Hacker in Rust"); assert_eq!(your_order.year, order_template.year); assert_eq!(your_order.made_by_phone, order_template.made_by_phone); diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 7cda5af..93b57fe 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,13 +1,5 @@ -// structs3.rs -// // Structs contain data, but can also have logic. In this exercise we have -// defined the Package struct and we want to test some logic attached to it. -// Make the code compile and the tests pass! -// -// Execute `rustlings hint structs3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// defined the `Package` struct and we want to test some logic attached to it. #[derive(Debug)] struct Package { @@ -17,29 +9,36 @@ struct Package { } impl Package { - fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Package { + fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self { if weight_in_grams < 10 { - // This is not how you should handle errors in Rust, - // but we will learn about error handling later. - panic!("Can not ship a package with weight below 10 grams.") - } else { - Package { - sender_country, - recipient_country, - weight_in_grams, - } + // This isn't how you should handle errors in Rust, but we will + // learn about error handling later. + panic!("Can't ship a package with weight below 10 grams"); + } + + Self { + sender_country, + recipient_country, + weight_in_grams, } } - fn is_international(&self) -> ??? { - // Something goes here... + // TODO: Add the correct return type to the function signature. + fn is_international(&self) { + // TODO: Read the tests that use this method to find out when a package + // is considered international. } - fn get_fees(&self, cents_per_gram: u32) -> ??? { - // Something goes here... + // TODO: Add the correct return type to the function signature. + fn get_fees(&self, cents_per_gram: u32) { + // TODO: Calculate the package's fees. } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs index ab87270..c65e641 100644 --- a/exercises/08_enums/enums1.rs +++ b/exercises/08_enums/enums1.rs @@ -1,12 +1,6 @@ -// enums1.rs -// -// No hints this time! ;) - -// I AM NOT DONE - #[derive(Debug)] enum Message { - // TODO: define a few types of messages as used below + // TODO: Define a few types of messages as used below. } fn main() { diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index 133c779..5a74991 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -1,13 +1,7 @@ -// enums2.rs -// -// Execute `rustlings hint enums2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +#[allow(dead_code)] #[derive(Debug)] enum Message { - // TODO: define the different variants used below + // TODO: Define the different variants used below. } #[derive(Debug)] @@ -18,7 +12,7 @@ struct Point { impl Message { fn call(&self) { - println!("{:?}", self); + println!("{self:?}"); } } diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index fffb9d0..2d0d82d 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -1,14 +1,5 @@ -// enums3.rs -// -// Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint enums3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - enum Message { - // TODO: implement the message variant types based on their usage below + // TODO: Implement the message variant types based on their usage below. } struct Point { @@ -35,25 +26,29 @@ impl State { } fn echo(&mut self, s: String) { - self.message = s + self.message = s; } - fn resize(&mut self, w: u8, h: u8) { - self.width = w; - self.height = h; + fn resize(&mut self, width: u8, height: u8) { + self.width = width; + self.height = height; } - fn move_position(&mut self, p: Point) { - self.position = p; + fn move_position(&mut self, point: Point) { + self.position = point; } fn process(&mut self, message: Message) { - // TODO: create a match expression to process the different message variants + // TODO: Create a match expression to process the different message variants. // Remember: When passing a tuple as a function argument, you'll need extra parentheses: - // fn function((t, u, p, l, e)) + // e.g. `foo((t, u, p, l, e))` } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -66,8 +61,9 @@ mod tests { height: 0, position: Point { x: 0, y: 0 }, color: (0, 0, 0), - message: "hello world".to_string(), + message: String::from("hello world"), }; + state.process(Message::ChangeColor(255, 0, 255)); state.process(Message::Echo(String::from("Hello world!"))); state.process(Message::Resize { w: 10, h: 30 }); @@ -79,7 +75,7 @@ mod tests { assert_eq!(state.height, 30); assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); - assert_eq!(state.quit, true); + assert!(state.quit); assert_eq!(state.message, "Hello world!"); } } diff --git a/exercises/09_strings/strings1.rs b/exercises/09_strings/strings1.rs index f50e1fa..6abdbb4 100644 --- a/exercises/09_strings/strings1.rs +++ b/exercises/09_strings/strings1.rs @@ -1,17 +1,9 @@ -// strings1.rs -// -// Make me compile without changing the function signature! -// -// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// TODO: Fix the compiler error without changing the function signature. +fn current_favorite_color() -> String { + "blue" +} fn main() { let answer = current_favorite_color(); - println!("My current favorite color is {}", answer); -} - -fn current_favorite_color() -> String { - "blue" + println!("My current favorite color is {answer}"); } diff --git a/exercises/09_strings/strings2.rs b/exercises/09_strings/strings2.rs index 4d95d16..93d9cb6 100644 --- a/exercises/09_strings/strings2.rs +++ b/exercises/09_strings/strings2.rs @@ -1,21 +1,14 @@ -// strings2.rs -// -// Make me compile without changing the function signature! -// -// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// TODO: Fix the compiler error in the `main` function without changing this function. +fn is_a_color_word(attempt: &str) -> bool { + attempt == "green" || attempt == "blue" || attempt == "red" +} fn main() { - let word = String::from("green"); // Try not changing this line :) + let word = String::from("green"); // Don't change this line. + if is_a_color_word(word) { println!("That is a color word I know!"); } else { println!("That is not a color word I know."); } } - -fn is_a_color_word(attempt: &str) -> bool { - attempt == "green" || attempt == "blue" || attempt == "red" -} diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index b29f932..39fce18 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -1,23 +1,17 @@ -// strings3.rs -// -// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -fn trim_me(input: &str) -> String { - // TODO: Remove whitespace from both ends of a string! - ??? +fn trim_me(input: &str) -> &str { + // TODO: Remove whitespace from both ends of a string. } fn compose_me(input: &str) -> String { - // TODO: Add " world!" to the string! There's multiple ways to do this! - ??? + // TODO: Add " world!" to the string! There are multiple ways to do this. } fn replace_me(input: &str) -> String { - // TODO: Replace "cars" in the string with "balloons"! - ??? + // TODO: Replace "cars" in the string with "balloons". +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] @@ -39,7 +33,13 @@ mod tests { #[test] fn replace_a_string() { - assert_eq!(replace_me("I think cars are cool"), "I think balloons are cool"); - assert_eq!(replace_me("I love to look at cars"), "I love to look at balloons"); + assert_eq!( + replace_me("I think cars are cool"), + "I think balloons are cool", + ); + assert_eq!( + replace_me("I love to look at cars"), + "I love to look at balloons", + ); } } diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs index e8c54ac..9d9eb48 100644 --- a/exercises/09_strings/strings4.rs +++ b/exercises/09_strings/strings4.rs @@ -1,30 +1,36 @@ -// strings4.rs -// -// Ok, here are a bunch of values-- some are `String`s, some are `&str`s. Your -// task is to call one of these two functions on each value depending on what -// you think each value is. That is, add either `string_slice` or `string` -// before the parentheses on each line. If you're right, it will compile! -// -// No hints this time! - -// I AM NOT DONE +// Calls of this function should be replaced with calls of `string_slice` or `string`. +fn placeholder() {} fn string_slice(arg: &str) { - println!("{}", arg); + println!("{arg}"); } fn string(arg: String) { - println!("{}", arg); + println!("{arg}"); } +// TODO: Here are a bunch of values - some are `String`, some are `&str`. +// Your task is to replace `placeholder(โฆ)` with either `string_slice(โฆ)` +// or `string(โฆ)` depending on what you think each value is. fn main() { - ???("blue"); - ???("red".to_string()); - ???(String::from("hi")); - ???("rust is fun!".to_owned()); - ???("nice weather".into()); - ???(format!("Interpolation {}", "Station")); - ???(&String::from("abc")[0..1]); - ???(" hello there ".trim()); - ???("Happy Monday!".to_string().replace("Mon", "Tues")); - ???("mY sHiFt KeY iS sTiCkY".to_lowercase()); + placeholder("blue"); + + placeholder("red".to_string()); + + placeholder(String::from("hi")); + + placeholder("rust is fun!".to_owned()); + + placeholder("nice weather".into()); + + placeholder(format!("Interpolation {}", "Station")); + + // WARNING: This is byte indexing, not character indexing. + // Character indexing can be done using `s.chars().nth(INDEX)`. + placeholder(&String::from("abc")[0..1]); + + placeholder(" hello there ".trim()); + + placeholder("Happy Monday!".replace("Mon", "Tues")); + + placeholder("mY sHiFt KeY iS sTiCkY".to_lowercase()); } diff --git a/exercises/10_modules/modules1.rs b/exercises/10_modules/modules1.rs index 9eb5a48..d97ab23 100644 --- a/exercises/10_modules/modules1.rs +++ b/exercises/10_modules/modules1.rs @@ -1,10 +1,4 @@ -// modules1.rs -// -// Execute `rustlings hint modules1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +// TODO: Fix the compiler error about calling a private function. mod sausage_factory { // Don't let anybody outside of this module see this! fn get_secret_recipe() -> String { diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs index 0415454..02eb80a 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -1,27 +1,20 @@ -// modules2.rs -// // You can bring module paths into scopes and provide new names for them with -// the 'use' and 'as' keywords. Fix these 'use' statements to make the code -// compile. -// -// Execute `rustlings hint modules2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// the `use` and `as` keywords. +#[allow(dead_code)] mod delicious_snacks { - // TODO: Fix these use statements - use self::fruits::PEAR as ??? - use self::veggies::CUCUMBER as ??? + // TODO: Add the following two `use` statements after fixing them. + // use self::fruits::PEAR as ???; + // use self::veggies::CUCUMBER as ???; mod fruits { - pub const PEAR: &'static str = "Pear"; - pub const APPLE: &'static str = "Apple"; + pub const PEAR: &str = "Pear"; + pub const APPLE: &str = "Apple"; } mod veggies { - pub const CUCUMBER: &'static str = "Cucumber"; - pub const CARROT: &'static str = "Carrot"; + pub const CUCUMBER: &str = "Cucumber"; + pub const CARROT: &str = "Carrot"; } } @@ -29,6 +22,6 @@ fn main() { println!( "favorite snacks: {} and {}", delicious_snacks::fruit, - delicious_snacks::veggie + delicious_snacks::veggie, ); } diff --git a/exercises/10_modules/modules3.rs b/exercises/10_modules/modules3.rs index f2bb050..691608d 100644 --- a/exercises/10_modules/modules3.rs +++ b/exercises/10_modules/modules3.rs @@ -1,17 +1,9 @@ -// modules3.rs -// -// You can use the 'use' keyword to bring module paths from modules from -// anywhere and especially from the Rust standard library into your scope. Bring -// SystemTime and UNIX_EPOCH from the std::time module. Bonus style points if -// you can do it with one line! -// -// Execute `rustlings hint modules3` or use the `hint` watch subcommand for a -// hint. +// You can use the `use` keyword to bring module paths from modules from +// anywhere and especially from the standard library into your scope. -// I AM NOT DONE - -// TODO: Complete this use statement -use ??? +// TODO: Bring `SystemTime` and `UNIX_EPOCH` from the `std::time` module into +// your scope. Bonus style points if you can do it with one line! +// use ???; fn main() { match SystemTime::now().duration_since(UNIX_EPOCH) { diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs index 80829ea..74001d0 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -1,31 +1,27 @@ -// hashmaps1.rs -// // A basket of fruits in the form of a hash map needs to be defined. The key // represents the name of the fruit and the value represents how many of that -// particular fruit is in the basket. You have to put at least three different -// types of fruits (e.g apple, banana, mango) in the basket and the total count -// of all the fruits should be at least five. -// -// Make me compile and pass the tests! -// -// Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// particular fruit is in the basket. You have to put at least 3 different +// types of fruits (e.g. apple, banana, mango) in the basket and the total count +// of all the fruits should be at least 5. use std::collections::HashMap; fn fruit_basket() -> HashMap<String, u32> { - let mut basket = // TODO: declare your hash map here. + // TODO: Declare the hash map. + // let mut basket = // Two bananas are already given for you :) basket.insert(String::from("banana"), 2); - // TODO: Put more fruits in your basket here. + // TODO: Put more fruits in your basket. basket } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index a592569..b3691b6 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -1,5 +1,3 @@ -// hashmaps2.rs -// // We're collecting different fruits to bake a delicious fruit cake. For this, // we have a basket, which we'll represent in the form of a hash map. The key // represents the name of each fruit we collect and the value represents how @@ -8,17 +6,10 @@ // must add fruit to the basket so that there is at least one of each kind and // more than 11 in total - we have a lot of mouths to feed. You are not allowed // to insert any more of these fruits! -// -// Make me pass the tests! -// -// Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE use std::collections::HashMap; -#[derive(Hash, PartialEq, Eq)] +#[derive(Hash, PartialEq, Eq, Debug)] enum Fruit { Apple, Banana, @@ -28,7 +19,7 @@ enum Fruit { } fn fruit_basket(basket: &mut HashMap<Fruit, u32>) { - let fruit_kinds = vec![ + let fruit_kinds = [ Fruit::Apple, Fruit::Banana, Fruit::Mango, @@ -43,18 +34,18 @@ fn fruit_basket(basket: &mut HashMap<Fruit, u32>) { } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; // Don't modify this function! fn get_fruit_basket() -> HashMap<Fruit, u32> { - let mut basket = HashMap::<Fruit, u32>::new(); - basket.insert(Fruit::Apple, 4); - basket.insert(Fruit::Mango, 2); - basket.insert(Fruit::Lychee, 5); - - basket + let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)]; + HashMap::from_iter(content) } #[test] @@ -81,13 +72,25 @@ mod tests { let count = basket.values().sum::<u32>(); assert!(count > 11); } - + #[test] fn all_fruit_types_in_basket() { + let fruit_kinds = [ + Fruit::Apple, + Fruit::Banana, + Fruit::Mango, + Fruit::Lychee, + Fruit::Pineapple, + ]; + let mut basket = get_fruit_basket(); fruit_basket(&mut basket); - for amount in basket.values() { - assert_ne!(amount, &0); + + for fruit_kind in fruit_kinds { + let Some(amount) = basket.get(&fruit_kind) else { + panic!("Fruit kind {fruit_kind:?} was not found in basket"); + }; + assert!(*amount > 0); } } } diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 08e977c..9f8fdd7 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -1,86 +1,77 @@ -// hashmaps3.rs -// // A list of scores (one per line) of a soccer match is given. Each line is of -// the form : "<team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>" -// Example: England,France,4,2 (England scored 4 goals, France 2). -// -// You have to build a scores table containing the name of the team, goals the -// team scored, and goals the team conceded. One approach to build the scores -// table is to use a Hashmap. The solution is partially written to use a -// Hashmap, complete it to pass the test. -// -// Make me pass the tests! +// the form "<team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>" +// Example: "England,France,4,2" (England scored 4 goals, France 2). // -// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// You have to build a scores table containing the name of the team, the total +// number of goals the team scored, and the total number of goals the team +// conceded. use std::collections::HashMap; // A structure to store the goal details of a team. +#[derive(Default)] struct Team { goals_scored: u8, goals_conceded: u8, } -fn build_scores_table(results: String) -> HashMap<String, Team> { +fn build_scores_table(results: &str) -> HashMap<&str, Team> { // The name of the team is the key and its associated struct is the value. - let mut scores: HashMap<String, Team> = HashMap::new(); + let mut scores = HashMap::new(); + + for line in results.lines() { + let mut split_iterator = line.split(','); + // NOTE: We use `unwrap` because we didn't deal with error handling yet. + let team_1_name = split_iterator.next().unwrap(); + let team_2_name = split_iterator.next().unwrap(); + let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap(); + let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); - for r in results.lines() { - let v: Vec<&str> = r.split(',').collect(); - let team_1_name = v[0].to_string(); - let team_1_score: u8 = v[2].parse().unwrap(); - let team_2_name = v[1].to_string(); - let team_2_score: u8 = v[3].parse().unwrap(); - // TODO: Populate the scores table with details extracted from the - // current line. Keep in mind that goals scored by team_1 - // will be the number of goals conceded from team_2, and similarly - // goals scored by team_2 will be the number of goals conceded by - // team_1. + // TODO: Populate the scores table with the extracted details. + // Keep in mind that goals scored by team 1 will be the number of goals + // conceded by team 2. Similarly, goals scored by team 2 will be the + // number of goals conceded by team 1. } + scores } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; - fn get_results() -> String { - let results = "".to_string() - + "England,France,4,2\n" - + "France,Italy,3,1\n" - + "Poland,Spain,2,0\n" - + "Germany,England,2,1\n"; - results - } + const RESULTS: &str = "England,France,4,2 +France,Italy,3,1 +Poland,Spain,2,0 +Germany,England,2,1 +England,Spain,1,0"; #[test] fn build_scores() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); - let mut keys: Vec<&String> = scores.keys().collect(); - keys.sort(); - assert_eq!( - keys, - vec!["England", "France", "Germany", "Italy", "Poland", "Spain"] - ); + assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"] + .into_iter() + .all(|team_name| scores.contains_key(team_name))); } #[test] fn validate_team_score_1() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); let team = scores.get("England").unwrap(); - assert_eq!(team.goals_scored, 5); + assert_eq!(team.goals_scored, 6); assert_eq!(team.goals_conceded, 4); } #[test] fn validate_team_score_2() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); let team = scores.get("Spain").unwrap(); assert_eq!(team.goals_scored, 0); - assert_eq!(team.goals_conceded, 2); + assert_eq!(team.goals_conceded, 3); } } diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index e131b48..9964807 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -1,19 +1,13 @@ -// options1.rs -// -// Execute `rustlings hint options1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - // This function returns how much icecream there is left in the fridge. -// If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them -// all, so there'll be no more left :( -fn maybe_icecream(time_of_day: u16) -> Option<u16> { - // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a - // value of 0 The Option output should gracefully handle cases where - // time_of_day > 23. - // TODO: Complete the function body - remember to return an Option! - ??? +// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, +// someone eats it all, so no icecream is left (value 0). Return `None` if +// `hour_of_day` is higher than 23. +fn maybe_icecream(hour_of_day: u16) -> Option<u16> { + // TODO: Complete the function body. +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] @@ -21,19 +15,22 @@ mod tests { use super::*; #[test] + fn raw_value() { + // TODO: Fix this test. How do you get the value contained in the + // Option? + let icecreams = maybe_icecream(12); + + assert_eq!(icecreams, 5); // Don't change this line. + } + + #[test] fn check_icecream() { + assert_eq!(maybe_icecream(0), Some(5)); assert_eq!(maybe_icecream(9), Some(5)); - assert_eq!(maybe_icecream(10), Some(5)); - assert_eq!(maybe_icecream(23), Some(0)); + assert_eq!(maybe_icecream(18), Some(5)); assert_eq!(maybe_icecream(22), Some(0)); + assert_eq!(maybe_icecream(23), Some(0)); + assert_eq!(maybe_icecream(24), None); assert_eq!(maybe_icecream(25), None); } - - #[test] - fn raw_value() { - // TODO: Fix this test. How do you get at the value contained in the - // Option? - let icecreams = maybe_icecream(12); - assert_eq!(icecreams, 5); - } } diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs index 4d998e7..07c27c6 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -1,9 +1,6 @@ -// options2.rs -// -// Execute `rustlings hint options2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +fn main() { + // You can optionally experiment here. +} #[cfg(test)] mod tests { @@ -12,7 +9,7 @@ mod tests { let target = "rustlings"; let optional_target = Some(target); - // TODO: Make this an if let statement whose value is "Some" type + // TODO: Make this an if-let statement whose value is `Some`. word = optional_target { assert_eq!(word, target); } @@ -23,15 +20,15 @@ mod tests { let range = 10; let mut optional_integers: Vec<Option<i8>> = vec![None]; - for i in 1..(range + 1) { + for i in 1..=range { optional_integers.push(Some(i)); } let mut cursor = range; - // TODO: make this a while let statement - remember that vector.pop also - // adds another layer of Option<T>. You can stack `Option<T>`s into - // while let and if let. + // TODO: Make this a while-let statement. Remember that `Vec::pop()` + // adds another layer of `Option`. You can do nested pattern matching + // in if-let and while-let statements. integer = optional_integers.pop() { assert_eq!(integer, cursor); cursor -= 1; diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs index 23c15ea..4cedb51 100644 --- a/exercises/12_options/options3.rs +++ b/exercises/12_options/options3.rs @@ -1,21 +1,17 @@ -// options3.rs -// -// Execute `rustlings hint options3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +#[derive(Debug)] struct Point { x: i32, y: i32, } fn main() { - let y: Option<Point> = Some(Point { x: 100, y: 200 }); + let optional_point = Some(Point { x: 100, y: 200 }); - match y { - Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y), - _ => panic!("no match!"), + // TODO: Fix the compiler error by adding something to this match statement. + match optional_point { + Some(p) => println!("Co-ordinates are {},{}", p.x, p.y), + _ => panic!("No match!"), } - y; // Fix without deleting this line. + + println!("{optional_point:?}"); // Don't change this line. } diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index 0ba59a5..ec7cb3c 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -1,25 +1,22 @@ -// errors1.rs -// -// This function refuses to generate text to be printed on a nametag if you pass -// it an empty string. It'd be nicer if it explained what the problem was, -// instead of just sometimes returning `None`. Thankfully, Rust has a similar -// construct to `Option` that can be used to express error conditions. Let's use -// it! -// -// Execute `rustlings hint errors1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -pub fn generate_nametag_text(name: String) -> Option<String> { +// TODO: This function refuses to generate text to be printed on a nametag if +// you pass it an empty string. It'd be nicer if it explained what the problem +// was instead of just returning `None`. Thankfully, Rust has a similar +// construct to `Option` that can be used to express error conditions. Change +// the function signature and body to return `Result<String, String>` instead +// of `Option<String>`. +fn generate_nametag_text(name: String) -> Option<String> { if name.is_empty() { // Empty names aren't allowed. None } else { - Some(format!("Hi! My name is {}", name)) + Some(format!("Hi! My name is {name}")) } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -27,17 +24,18 @@ mod tests { #[test] fn generates_nametag_text_for_a_nonempty_name() { assert_eq!( - generate_nametag_text("Beyoncรฉ".into()), - Ok("Hi! My name is Beyoncรฉ".into()) + generate_nametag_text("Beyoncรฉ".to_string()).as_deref(), + Ok("Hi! My name is Beyoncรฉ"), ); } #[test] fn explains_why_generating_nametag_text_fails() { assert_eq!( - generate_nametag_text("".into()), - // Don't change this line - Err("`name` was empty; it must be nonempty.".into()) + generate_nametag_text(String::new()) + .as_ref() + .map_err(|e| e.as_str()), + Err("Empty names aren't allowed"), ); } } diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index 631fe67..defe359 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -1,39 +1,39 @@ -// errors2.rs -// // Say we're writing a game where you can buy items with tokens. All items cost // 5 tokens, and whenever you purchase items there is a processing fee of 1 // token. A player of the game will type in how many items they want to buy, and // the `total_cost` function will calculate the total cost of the items. Since -// the player typed in the quantity, though, we get it as a string-- and they -// might have typed anything, not just numbers! +// the player typed in the quantity, we get it as a string. They might have +// typed anything, not just numbers! // -// Right now, this function isn't handling the error case at all (and isn't -// handling the success case properly either). What we want to do is: if we call -// the `total_cost` function on a string that is not a number, that function -// will return a `ParseIntError`, and in that case, we want to immediately -// return that error from our function and not try to multiply and add. +// Right now, this function isn't handling the error case at all. What we want +// to do is: If we call the `total_cost` function on a string that is not a +// number, that function will return a `ParseIntError`. In that case, we want to +// immediately return that error from our function and not try to multiply and +// add. // -// There are at least two ways to implement this that are both correct-- but one +// There are at least two ways to implement this that are both correct. But one // is a lot shorter! -// -// Execute `rustlings hint errors2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE use std::num::ParseIntError; -pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> { +fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> { let processing_fee = 1; let cost_per_item = 5; + + // TODO: Handle the error case as described above. let qty = item_quantity.parse::<i32>(); Ok(qty * cost_per_item + processing_fee) } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; + use std::num::IntErrorKind; #[test] fn item_quantity_is_a_valid_number() { @@ -43,8 +43,8 @@ mod tests { #[test] fn item_quantity_is_an_invalid_number() { assert_eq!( - total_cost("beep boop").unwrap_err().to_string(), - "invalid digit found in string" + total_cost("beep boop").unwrap_err().kind(), + &IntErrorKind::InvalidDigit, ); } } diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index d42d3b1..33a7b87 100644 --- a/exercises/13_error_handling/errors3.rs +++ b/exercises/13_error_handling/errors3.rs @@ -1,16 +1,20 @@ -// errors3.rs -// // This is a program that is trying to use a completed version of the // `total_cost` function from the previous exercise. It's not working though! // Why not? What should we do to fix it? -// -// Execute `rustlings hint errors3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE use std::num::ParseIntError; +// Don't change this function. +fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> { + let processing_fee = 1; + let cost_per_item = 5; + let qty = item_quantity.parse::<i32>()?; + + Ok(qty * cost_per_item + processing_fee) +} + +// TODO: Fix the compiler error by changing the signature and body of the +// `main` function. fn main() { let mut tokens = 100; let pretend_user_input = "8"; @@ -21,14 +25,6 @@ fn main() { println!("You can't afford that many!"); } else { tokens -= cost; - println!("You now have {} tokens.", tokens); + println!("You now have {tokens} tokens."); } } - -pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> { - let processing_fee = 1; - let cost_per_item = 5; - let qty = item_quantity.parse::<i32>()?; - - Ok(qty * cost_per_item + processing_fee) -} diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index d6d6fcb..e41d594 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -1,12 +1,4 @@ -// errors4.rs -// -// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -#[derive(PartialEq, Debug)] -struct PositiveNonzeroInteger(u64); +#![allow(clippy::comparison_chain)] #[derive(PartialEq, Debug)] enum CreationError { @@ -14,19 +6,34 @@ enum CreationError { Zero, } +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + impl PositiveNonzeroInteger { - fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> { - // Hmm... Why is this always returning an Ok value? - Ok(PositiveNonzeroInteger(value as u64)) + fn new(value: i64) -> Result<Self, CreationError> { + // TODO: This function shouldn't always return an `Ok`. + Ok(Self(value as u64)) } } -#[test] -fn test_creation() { - assert!(PositiveNonzeroInteger::new(10).is_ok()); - assert_eq!( - Err(CreationError::Negative), - PositiveNonzeroInteger::new(-10) - ); - assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_creation() { + assert_eq!( + PositiveNonzeroInteger::new(10), + Ok(PositiveNonzeroInteger(10)), + ); + assert_eq!( + PositiveNonzeroInteger::new(-10), + Err(CreationError::Negative), + ); + assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero)); + } } diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index 92461a7..d0044db 100644 --- a/exercises/13_error_handling/errors5.rs +++ b/exercises/13_error_handling/errors5.rs @@ -1,45 +1,18 @@ -// errors5.rs -// -// This program uses an altered version of the code from errors4. -// -// This exercise uses some concepts that we won't get to until later in the -// course, like `Box` and the `From` trait. It's not important to understand -// them in detail right now, but you can read ahead if you like. For now, think -// of the `Box<dyn ???>` type as an "I want anything that does ???" type, which, -// given Rust's usual standards for runtime safety, should strike you as -// somewhat lenient! +// This exercise is an altered version of the `errors4` exercise. It uses some +// concepts that we won't get to until later in the course, like `Box` and the +// `From` trait. It's not important to understand them in detail right now, but +// you can read ahead if you like. For now, think of the `Box<dyn ???>` type as +// an "I want anything that does ???" type. // // In short, this particular use case for boxes is for when you want to own a // value and you care only that it is a type which implements a particular -// trait. To do so, The Box is declared as of type Box<dyn Trait> where Trait is -// the trait the compiler looks for on any value used in that context. For this -// exercise, that context is the potential errors which can be returned in a -// Result. -// -// What can we use to describe both errors? In other words, is there a trait -// which both errors implement? -// -// Execute `rustlings hint errors5` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where +// `Trait` is the trait the compiler looks for on any value used in that +// context. For this exercise, that context is the potential errors which +// can be returned in a `Result`. -use std::error; +use std::error::Error; use std::fmt; -use std::num::ParseIntError; - -// TODO: update the return type of `main()` to make this compile. -fn main() -> Result<(), Box<dyn ???>> { - let pretend_user_input = "42"; - let x: i64 = pretend_user_input.parse()?; - println!("output={:?}", PositiveNonzeroInteger::new(x)?); - Ok(()) -} - -// Don't change anything below this line. - -#[derive(PartialEq, Debug)] -struct PositiveNonzeroInteger(u64); #[derive(PartialEq, Debug)] enum CreationError { @@ -47,17 +20,7 @@ enum CreationError { Zero, } -impl PositiveNonzeroInteger { - fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> { - match value { - x if x < 0 => Err(CreationError::Negative), - x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)), - } - } -} - -// This is required so that `CreationError` can implement `error::Error`. +// This is required so that `CreationError` can implement `Error`. impl fmt::Display for CreationError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let description = match *self { @@ -68,4 +31,26 @@ impl fmt::Display for CreationError { } } -impl error::Error for CreationError {} +impl Error for CreationError {} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> { + match value { + 0 => Err(CreationError::Zero), + x if x < 0 => Err(CreationError::Negative), + x => Ok(PositiveNonzeroInteger(x as u64)), + } + } +} + +// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we +// use to describe both errors? Is there a trait which both errors implement? +fn main() { + let pretend_user_input = "42"; + let x: i64 = pretend_user_input.parse()?; + println!("output={:?}", PositiveNonzeroInteger::new(x)?); + Ok(()) +} diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index aaf0948..b656c61 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -1,19 +1,18 @@ -// errors6.rs -// -// Using catch-all error types like `Box<dyn error::Error>` isn't recommended -// for library code, where callers might want to make decisions based on the -// error content, instead of printing it out or propagating it further. Here, we -// define a custom error type to make it possible for callers to decide what to -// do next when our function returns an error. -// -// Execute `rustlings hint errors6` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// Using catch-all error types like `Box<dyn Error>` isn't recommended for +// library code where callers might want to make decisions based on the error +// content instead of printing it out or propagating it further. Here, we define +// a custom error type to make it possible for callers to decide what to do next +// when our function returns an error. use std::num::ParseIntError; -// This is a custom error type that we will be using in `parse_pos_nonzero()`. +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// A custom error type that we will be using in `PositiveNonzeroInteger::parse`. #[derive(PartialEq, Debug)] enum ParsePosNonzeroError { Creation(CreationError), @@ -21,39 +20,36 @@ enum ParsePosNonzeroError { } impl ParsePosNonzeroError { - fn from_creation(err: CreationError) -> ParsePosNonzeroError { - ParsePosNonzeroError::Creation(err) + fn from_creation(err: CreationError) -> Self { + Self::Creation(err) } - // TODO: add another error conversion function here. - // fn from_parseint... -} -fn parse_pos_nonzero(s: &str) -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> { - // TODO: change this to return an appropriate error instead of panicking - // when `parse()` returns an error. - let x: i64 = s.parse().unwrap(); - PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation) + // TODO: Add another error conversion function here. + // fn from_parseint(???) -> Self { ??? } } -// Don't change anything below this line. - #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); -#[derive(PartialEq, Debug)] -enum CreationError { - Negative, - Zero, -} - impl PositiveNonzeroInteger { - fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> { + fn new(value: i64) -> Result<Self, CreationError> { match value { x if x < 0 => Err(CreationError::Negative), - x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)), + 0 => Err(CreationError::Zero), + x => Ok(Self(x as u64)), } } + + fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> { + // TODO: change this to return an appropriate error instead of panicking + // when `parse()` returns an error. + let x: i64 = s.parse().unwrap(); + Self::new(x).map_err(ParsePosNonzeroError::from_creation) + } +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] @@ -62,33 +58,32 @@ mod test { #[test] fn test_parse_error() { - // We can't construct a ParseIntError, so we have to pattern match. assert!(matches!( - parse_pos_nonzero("not a number"), - Err(ParsePosNonzeroError::ParseInt(_)) + PositiveNonzeroInteger::parse("not a number"), + Err(ParsePosNonzeroError::ParseInt(_)), )); } #[test] fn test_negative() { assert_eq!( - parse_pos_nonzero("-555"), - Err(ParsePosNonzeroError::Creation(CreationError::Negative)) + PositiveNonzeroInteger::parse("-555"), + Err(ParsePosNonzeroError::Creation(CreationError::Negative)), ); } #[test] fn test_zero() { assert_eq!( - parse_pos_nonzero("0"), - Err(ParsePosNonzeroError::Creation(CreationError::Zero)) + PositiveNonzeroInteger::parse("0"), + Err(ParsePosNonzeroError::Creation(CreationError::Zero)), ); } #[test] fn test_positive() { - let x = PositiveNonzeroInteger::new(42); - assert!(x.is_ok()); - assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap())); + let x = PositiveNonzeroInteger::new(42).unwrap(); + assert_eq!(x.0, 42); + assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x)); } } diff --git a/exercises/14_generics/README.md b/exercises/14_generics/README.md index de46d50..72cff3f 100644 --- a/exercises/14_generics/README.md +++ b/exercises/14_generics/README.md @@ -1,7 +1,7 @@ # Generics Generics is the topic of generalizing types and functionalities to broader cases. -This is extremely useful for reducing code duplication in many ways, but can call for rather involving syntax. +This is extremely useful for reducing code duplication in many ways, but can call for some rather involved syntax. Namely, being generic requires taking great care to specify over which types a generic type is actually considered valid. The simplest and most common use of generics is for type parameters. diff --git a/exercises/14_generics/generics1.rs b/exercises/14_generics/generics1.rs index 35c1d2f..87ed990 100644 --- a/exercises/14_generics/generics1.rs +++ b/exercises/14_generics/generics1.rs @@ -1,14 +1,18 @@ -// generics1.rs -// -// This shopping list program isn't compiling! Use your knowledge of generics to -// fix it. -// -// Execute `rustlings hint generics1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// `Vec<T>` is generic over the type `T`. In most cases, the compiler is able to +// infer `T`, for example after pushing a value with a concrete type to the vector. +// But in this exercise, the compiler needs some help through a type annotation. fn main() { - let mut shopping_list: Vec<?> = Vec::new(); - shopping_list.push("milk"); + // TODO: Fix the compiler error by annotating the type of the vector + // `Vec<T>`. Choose `T` as some integer type that can be created from + // `u8` and `i8`. + let mut numbers = Vec::new(); + + // Don't change the lines below. + let n1: u8 = 42; + numbers.push(n1.into()); + let n2: i8 = -1; + numbers.push(n2.into()); + + println!("{numbers:?}"); } diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs index 074cd93..8908725 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -1,23 +1,20 @@ -// generics2.rs -// // This powerful wrapper provides the ability to store a positive integer value. -// Rewrite it using generics so that it supports wrapping ANY type. -// -// Execute `rustlings hint generics2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +// TODO: Rewrite it using a generic so that it supports wrapping ANY type. struct Wrapper { value: u32, } +// TODO: Adapt the struct's implementation to be generic over the wrapped value. impl Wrapper { - pub fn new(value: u32) -> Self { + fn new(value: u32) -> Self { Wrapper { value } } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/15_traits/traits1.rs b/exercises/15_traits/traits1.rs index 37dfcbf..85be17e 100644 --- a/exercises/15_traits/traits1.rs +++ b/exercises/15_traits/traits1.rs @@ -1,26 +1,17 @@ -// traits1.rs -// -// Time to implement some traits! Your task is to implement the trait -// `AppendBar` for the type `String`. The trait AppendBar has only one function, -// which appends "Bar" to any object implementing this trait. -// -// Execute `rustlings hint traits1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +// The trait `AppendBar` has only one function which appends "Bar" to any object +// implementing this trait. trait AppendBar { fn append_bar(self) -> Self; } impl AppendBar for String { - // TODO: Implement `AppendBar` for type `String`. + // TODO: Implement `AppendBar` for the type `String`. } fn main() { let s = String::from("Foo"); let s = s.append_bar(); - println!("s: {}", s); + println!("s: {s}"); } #[cfg(test)] @@ -29,14 +20,11 @@ mod tests { #[test] fn is_foo_bar() { - assert_eq!(String::from("Foo").append_bar(), String::from("FooBar")); + assert_eq!(String::from("Foo").append_bar(), "FooBar"); } #[test] fn is_bar_bar() { - assert_eq!( - String::from("").append_bar().append_bar(), - String::from("BarBar") - ); + assert_eq!(String::from("").append_bar().append_bar(), "BarBar"); } } diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index 3e35f8e..d724dc2 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -1,20 +1,13 @@ -// traits2.rs -// -// Your task is to implement the trait `AppendBar` for a vector of strings. To -// implement this trait, consider for a moment what it means to 'append "Bar"' -// to a vector of strings. -// -// No boiler plate code this time, you can do this! -// -// Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE - trait AppendBar { fn append_bar(self) -> Self; } -// TODO: Implement trait `AppendBar` for a vector of strings. +// TODO: Implement the trait `AppendBar` for a vector of strings. +// `append_bar` should push the string "Bar" into the vector. + +fn main() { + // You can optionally experiment here. +} #[cfg(test)] mod tests { @@ -23,7 +16,7 @@ mod tests { #[test] fn is_vec_pop_eq_bar() { let mut foo = vec![String::from("Foo")].append_bar(); - assert_eq!(foo.pop().unwrap(), String::from("Bar")); - assert_eq!(foo.pop().unwrap(), String::from("Foo")); + assert_eq!(foo.pop().unwrap(), "Bar"); + assert_eq!(foo.pop().unwrap(), "Foo"); } } diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 4e2b06b..2e8969e 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -1,16 +1,10 @@ -// traits3.rs -// -// Your task is to implement the Licensed trait for both structures and have -// them return the same information without writing the same function twice. -// -// Consider what you can add to the Licensed trait. -// -// Execute `rustlings hint traits3` or use the `hint` watch subcommand for a -// hint. +#![allow(dead_code)] -// I AM NOT DONE - -pub trait Licensed { +trait Licensed { + // TODO: Add a default implementation for `licensing_info` so that + // implementors like the two structs below can share that default behavior + // without repeating the function. + // The default license information should be the string "Default license". fn licensing_info(&self) -> String; } @@ -22,8 +16,12 @@ struct OtherSoftware { version_number: String, } -impl Licensed for SomeSoftware {} // Don't edit this line -impl Licensed for OtherSoftware {} // Don't edit this line +impl Licensed for SomeSoftware {} // Don't edit this line. +impl Licensed for OtherSoftware {} // Don't edit this line. + +fn main() { + // You can optionally experiment here. +} #[cfg(test)] mod tests { @@ -31,7 +29,7 @@ mod tests { #[test] fn is_licensing_info_the_same() { - let licensing_info = String::from("Some information"); + let licensing_info = "Default license"; let some_software = SomeSoftware { version_number: 1 }; let other_software = OtherSoftware { version_number: "v2.0.0".to_string(), diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index 4bda3e5..80092a6 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -1,30 +1,22 @@ -// traits4.rs -// -// Your task is to replace the '??' sections so the code compiles. -// -// Don't change any line other than the marked one. -// -// Execute `rustlings hint traits4` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -pub trait Licensed { +trait Licensed { fn licensing_info(&self) -> String { - "some information".to_string() + "Default license".to_string() } } -struct SomeSoftware {} - -struct OtherSoftware {} +struct SomeSoftware; +struct OtherSoftware; impl Licensed for SomeSoftware {} impl Licensed for OtherSoftware {} -// YOU MAY ONLY CHANGE THE NEXT LINE -fn compare_license_types(software: ??, software_two: ??) -> bool { - software.licensing_info() == software_two.licensing_info() +// TODO: Fix the compiler error by only changing the signature of this function. +fn compare_license_types(software1: ???, software2: ???) -> bool { + software1.licensing_info() == software2.licensing_info() +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] @@ -33,17 +25,11 @@ mod tests { #[test] fn compare_license_information() { - let some_software = SomeSoftware {}; - let other_software = OtherSoftware {}; - - assert!(compare_license_types(some_software, other_software)); + assert!(compare_license_types(SomeSoftware, OtherSoftware)); } #[test] fn compare_license_information_backwards() { - let some_software = SomeSoftware {}; - let other_software = OtherSoftware {}; - - assert!(compare_license_types(other_software, some_software)); + assert!(compare_license_types(OtherSoftware, SomeSoftware)); } } diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index df18380..5b356ac 100644 --- a/exercises/15_traits/traits5.rs +++ b/exercises/15_traits/traits5.rs @@ -1,40 +1,39 @@ -// traits5.rs -// -// Your task is to replace the '??' sections so the code compiles. -// -// Don't change any line other than the marked one. -// -// Execute `rustlings hint traits5` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -pub trait SomeTrait { +trait SomeTrait { fn some_function(&self) -> bool { true } } -pub trait OtherTrait { +trait OtherTrait { fn other_function(&self) -> bool { true } } -struct SomeStruct {} -struct OtherStruct {} - +struct SomeStruct; impl SomeTrait for SomeStruct {} impl OtherTrait for SomeStruct {} + +struct OtherStruct; impl SomeTrait for OtherStruct {} impl OtherTrait for OtherStruct {} -// YOU MAY ONLY CHANGE THE NEXT LINE -fn some_func(item: ??) -> bool { +// TODO: Fix the compiler error by only changing the signature of this function. +fn some_func(item: ???) -> bool { item.some_function() && item.other_function() } fn main() { - some_func(SomeStruct {}); - some_func(OtherStruct {}); + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_some_func() { + assert!(some_func(SomeStruct)); + assert!(some_func(OtherStruct)); + } } diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs index 87bde49..19e2d39 100644 --- a/exercises/16_lifetimes/lifetimes1.rs +++ b/exercises/16_lifetimes/lifetimes1.rs @@ -1,15 +1,9 @@ -// lifetimes1.rs -// // The Rust compiler needs to know how to check whether supplied references are // valid, so that it can let the programmer know if a reference is at risk of // going out of scope before it is used. Remember, references are borrows and do // not own their own data. What if their owner goes out of scope? -// -// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// TODO: Fix the compiler error by updating the function signature. fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x @@ -19,9 +13,16 @@ fn longest(x: &str, y: &str) -> &str { } fn main() { - let string1 = String::from("abcd"); - let string2 = "xyz"; + // You can optionally experiment here. +} - let result = longest(string1.as_str(), string2); - println!("The longest string is '{}'", result); +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_longest() { + assert_eq!(longest("abcd", "123"), "abcd"); + assert_eq!(longest("abc", "1234"), "1234"); + } } diff --git a/exercises/16_lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs index 4f3d8c1..de5a5df 100644 --- a/exercises/16_lifetimes/lifetimes2.rs +++ b/exercises/16_lifetimes/lifetimes2.rs @@ -1,13 +1,4 @@ -// lifetimes2.rs -// -// So if the compiler is just validating the references passed to the annotated -// parameters and the return type, what do we need to change? -// -// Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +// Don't change this function. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x @@ -17,11 +8,13 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { } fn main() { + // TODO: Fix the compiler error by moving one line. + let string1 = String::from("long string is long"); let result; { let string2 = String::from("xyz"); - result = longest(string1.as_str(), string2.as_str()); + result = longest(&string1, &string2); } - println!("The longest string is '{}'", result); + println!("The longest string is '{result}'"); } diff --git a/exercises/16_lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs index 9c59f9c..1cc2759 100644 --- a/exercises/16_lifetimes/lifetimes3.rs +++ b/exercises/16_lifetimes/lifetimes3.rs @@ -1,21 +1,16 @@ -// lifetimes3.rs -// // Lifetimes are also needed when structs hold references. -// -// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// TODO: Fix the compiler errors about the struct. struct Book { author: &str, title: &str, } fn main() { - let name = String::from("Jill Smith"); - let title = String::from("Fish Flying"); - let book = Book { author: &name, title: &title }; + let book = Book { + author: "George Orwell", + title: "1984", + }; println!("{} by {}", book.title, book.author); } diff --git a/exercises/17_tests/tests1.rs b/exercises/17_tests/tests1.rs index 810277a..7529f9f 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -1,21 +1,23 @@ -// tests1.rs -// // Tests are important to ensure that your code does what you think it should -// do. Tests can be run on this file with the following command: rustlings run -// tests1 -// -// This test has a problem with it -- make the test compile! Make the test pass! -// Make the test fail! -// -// Execute `rustlings hint tests1` or use the `hint` watch subcommand for a -// hint. +// do. -// I AM NOT DONE +fn is_even(n: i64) -> bool { + n % 2 == 0 +} + +fn main() { + // You can optionally experiment here. +} #[cfg(test)] mod tests { + // TODO: Import `is_even`. You can use a wildcard to import everything in + // the outer module. + #[test] fn you_can_assert() { + // TODO: Test the function `is_even` with some values. + assert!(); assert!(); } } diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index f8024e9..0c6573e 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -1,17 +1,23 @@ -// tests2.rs -// -// This test has a problem with it -- make the test compile! Make the test pass! -// Make the test fail! -// -// Execute `rustlings hint tests2` or use the `hint` watch subcommand for a -// hint. +// Calculates the power of 2 using a bit shift. +// `1 << n` is equivalent to "2 to the power of n". +fn power_of_2(n: u8) -> u64 { + 1 << n +} -// I AM NOT DONE +fn main() { + // You can optionally experiment here. +} #[cfg(test)] mod tests { + use super::*; + #[test] fn you_can_assert_eq() { + // TODO: Test the function `power_of_2` with some values. + assert_eq!(); + assert_eq!(); + assert_eq!(); assert_eq!(); } } diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index 4013e38..ec99479 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -1,16 +1,23 @@ -// tests3.rs -// -// This test isn't testing our function -- make it do that in such a way that -// the test passes. Then write a second test that tests whether we get the -// result we expect to get when we call `is_even(5)`. -// -// Execute `rustlings hint tests3` or use the `hint` watch subcommand for a -// hint. +struct Rectangle { + width: i32, + height: i32, +} + +impl Rectangle { + // Don't change this function. + fn new(width: i32, height: i32) -> Self { + if width <= 0 || height <= 0 { + // Returning a `Result` would be better here. But we want to learn + // how to test functions that can panic. + panic!("Rectangle width and height can't be negative"); + } -// I AM NOT DONE + Rectangle { width, height } + } +} -pub fn is_even(num: i32) -> bool { - num % 2 == 0 +fn main() { + // You can optionally experiment here. } #[cfg(test)] @@ -18,12 +25,25 @@ mod tests { use super::*; #[test] - fn is_true_when_even() { - assert!(); + fn correct_width_and_height() { + // TODO: This test should check if the rectangle has the size that we + // pass to its constructor. + let rect = Rectangle::new(10, 20); + assert_eq!(todo!(), 10); // Check width + assert_eq!(todo!(), 20); // Check height + } + + // TODO: This test should check if the program panics when we try to create + // a rectangle with negative width. + #[test] + fn negative_width() { + let _rect = Rectangle::new(-10, 10); } + // TODO: This test should check if the program panics when we try to create + // a rectangle with negative height. #[test] - fn is_false_when_odd() { - assert!(); + fn negative_height() { + let _rect = Rectangle::new(10, -10); } } diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs deleted file mode 100644 index 935d0db..0000000 --- a/exercises/17_tests/tests4.rs +++ /dev/null @@ -1,48 +0,0 @@ -// tests4.rs -// -// Make sure that we're testing for the correct conditions! -// -// Execute `rustlings hint tests4` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -struct Rectangle { - width: i32, - height: i32 -} - -impl Rectangle { - // Only change the test functions themselves - pub fn new(width: i32, height: i32) -> Self { - if width <= 0 || height <= 0 { - panic!("Rectangle width and height cannot be negative!") - } - Rectangle {width, height} - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn correct_width_and_height() { - // This test should check if the rectangle is the size that we pass into its constructor - let rect = Rectangle::new(10, 20); - assert_eq!(???, 10); // check width - assert_eq!(???, 20); // check height - } - - #[test] - fn negative_width() { - // This test should check if program panics when we try to create rectangle with negative width - let _rect = Rectangle::new(-10, 10); - } - - #[test] - fn negative_height() { - // This test should check if program panics when we try to create rectangle with negative height - let _rect = Rectangle::new(10, -10); - } -} diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index 31076bb..ca937ed 100644 --- a/exercises/18_iterators/iterators1.rs +++ b/exercises/18_iterators/iterators1.rs @@ -1,26 +1,25 @@ -// iterators1.rs -// // When performing operations on elements within a collection, iterators are // essential. This module helps you get familiar with the structure of using an // iterator and how to go through elements within an iterable collection. -// -// Make me compile by filling in the `???`s -// -// Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a -// hint. -// I AM NOT DONE - -#[test] fn main() { - let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn iterators() { + let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; - let mut my_iterable_fav_fruits = ???; // TODO: Step 1 + // TODO: Create an iterator over the array. + let mut fav_fruits_iterator = todo!(); - assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 2 - assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 3 - assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 4 + assert_eq!(fav_fruits_iterator.next(), Some(&"banana")); + assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` + assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); + assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` + assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); + assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` + } } diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index dda82a0..5903e65 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -1,38 +1,32 @@ -// iterators2.rs -// // In this exercise, you'll learn some of the unique advantages that iterators -// can offer. Follow the steps to complete the exercise. -// -// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a -// hint. +// can offer. -// I AM NOT DONE - -// Step 1. -// Complete the `capitalize_first` function. +// TODO: Complete the `capitalize_first` function. // "hello" -> "Hello" -pub fn capitalize_first(input: &str) -> String { - let mut c = input.chars(); - match c.next() { +fn capitalize_first(input: &str) -> String { + let mut chars = input.chars(); + match chars.next() { None => String::new(), - Some(first) => ???, + Some(first) => todo!(), } } -// Step 2. -// Apply the `capitalize_first` function to a slice of string slices. +// TODO: Apply the `capitalize_first` function to a slice of string slices. // Return a vector of strings. // ["hello", "world"] -> ["Hello", "World"] -pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> { - vec![] +fn capitalize_words_vector(words: &[&str]) -> Vec<String> { + // ??? } -// Step 3. -// Apply the `capitalize_first` function again to a slice of string slices. -// Return a single string. +// TODO: Apply the `capitalize_first` function again to a slice of string +// slices. Return a single string. // ["hello", " ", "world"] -> "Hello World" -pub fn capitalize_words_string(words: &[&str]) -> String { - String::new() +fn capitalize_words_string(words: &[&str]) -> String { + // ??? +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 29fa23a..65a0573 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -1,50 +1,33 @@ -// iterators3.rs -// -// This is a bigger exercise than most of the others! You can do it! Here is -// your mission, should you choose to accept it: -// 1. Complete the divide function to get the first four tests to pass. -// 2. Get the remaining tests to pass by completing the result_with_list and -// list_of_results functions. -// -// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - #[derive(Debug, PartialEq, Eq)] -pub enum DivisionError { - NotDivisible(NotDivisibleError), +enum DivisionError { DivideByZero, + NotDivisible, } -#[derive(Debug, PartialEq, Eq)] -pub struct NotDivisibleError { - dividend: i32, - divisor: i32, -} - -// Calculate `a` divided by `b` if `a` is evenly divisible by `b`. +// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`. // Otherwise, return a suitable error. -pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> { +fn divide(a: i32, b: i32) -> Result<i32, DivisionError> { todo!(); } -// Complete the function and return a value of the correct type so the test -// passes. -// Desired output: Ok([1, 11, 1426, 3]) -fn result_with_list() -> () { - let numbers = vec![27, 297, 38502, 81]; +// TODO: Add the correct return type and complete the function body. +// Desired output: `Ok([1, 11, 1426, 3])` +fn result_with_list() { + let numbers = [27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); } -// Complete the function and return a value of the correct type so the test -// passes. -// Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)] -fn list_of_results() -> () { - let numbers = vec![27, 297, 38502, 81]; +// TODO: Add the correct return type and complete the function body. +// Desired output: `[Ok(1), Ok(11), Ok(1426), Ok(3)]` +fn list_of_results() { + let numbers = [27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -55,19 +38,13 @@ mod tests { } #[test] - fn test_not_divisible() { - assert_eq!( - divide(81, 6), - Err(DivisionError::NotDivisible(NotDivisibleError { - dividend: 81, - divisor: 6 - })) - ); + fn test_divide_by_0() { + assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); } #[test] - fn test_divide_by_0() { - assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); + fn test_not_divisible() { + assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); } #[test] @@ -77,14 +54,11 @@ mod tests { #[test] fn test_result_with_list() { - assert_eq!(format!("{:?}", result_with_list()), "Ok([1, 11, 1426, 3])"); + assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]); } #[test] fn test_list_of_results() { - assert_eq!( - format!("{:?}", list_of_results()), - "[Ok(1), Ok(11), Ok(1426), Ok(3)]" - ); + assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]); } } diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 79e1692..8381dbb 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -1,20 +1,16 @@ -// iterators4.rs -// -// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -pub fn factorial(num: u64) -> u64 { - // Complete this function to return the factorial of num +fn factorial(num: u64) -> u64 { + // TODO: Complete this function to return the factorial of `num`. // Do not use: - // - return + // - early returns (using the `return` keyword explicitly) // Try not to use: - // - imperative style loops (for, while) + // - imperative style loops (for/while) // - additional variables // For an extra challenge, don't use: // - recursion - // Execute `rustlings hint iterators4` for hints. +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] @@ -23,20 +19,20 @@ mod tests { #[test] fn factorial_of_0() { - assert_eq!(1, factorial(0)); + assert_eq!(factorial(0), 1); } #[test] fn factorial_of_1() { - assert_eq!(1, factorial(1)); + assert_eq!(factorial(1), 1); } #[test] fn factorial_of_2() { - assert_eq!(2, factorial(2)); + assert_eq!(factorial(2), 2); } #[test] fn factorial_of_4() { - assert_eq!(24, factorial(4)); + assert_eq!(factorial(4), 24); } } diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index a062ee4..7e434cc 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -1,17 +1,8 @@ -// iterators5.rs -// -// Let's define a simple model to track Rustlings exercise progress. Progress +// Let's define a simple model to track Rustlings' exercise progress. Progress // will be modelled using a hash map. The name of the exercise is the key and // the progress is the value. Two counting functions were created to count the // number of exercises with a given progress. Recreate this counting -// functionality using iterators. Try not to use imperative loops (for, while). -// Only the two iterator methods (count_iterator and count_collection_iterator) -// need to be modified. -// -// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// functionality using iterators. Try to not use imperative loops (for/while). use std::collections::HashMap; @@ -25,24 +16,25 @@ enum Progress { fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize { let mut count = 0; for val in map.values() { - if val == &value { + if *val == value { count += 1; } } count } +// TODO: Implement the functionality of `count_for` but with an iterator instead +// of a `for` loop. fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize { - // map is a hashmap with String keys and Progress values. - // map = { "variables1": Complete, "from_str": None, ... } - todo!(); + // `map` is a hash map with `String` keys and `Progress` values. + // map = { "variables1": Complete, "from_str": None, โฆ } } fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize { let mut count = 0; for map in collection { for val in map.values() { - if val == &value { + if *val == value { count += 1; } } @@ -50,43 +42,77 @@ fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progres count } +// TODO: Implement the functionality of `count_collection_for` but with an +// iterator instead of a `for` loop. fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize { - // collection is a slice of hashmaps. - // collection = [{ "variables1": Complete, "from_str": None, ... }, - // { "variables2": Complete, ... }, ... ] - todo!(); + // `collection` is a slice of hash maps. + // collection = [{ "variables1": Complete, "from_str": None, โฆ }, + // { "variables2": Complete, โฆ }, โฆ ] +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] mod tests { use super::*; + fn get_map() -> HashMap<String, Progress> { + use Progress::*; + + let mut map = HashMap::new(); + map.insert(String::from("variables1"), Complete); + map.insert(String::from("functions1"), Complete); + map.insert(String::from("hashmap1"), Complete); + map.insert(String::from("arc1"), Some); + map.insert(String::from("as_ref_mut"), None); + map.insert(String::from("from_str"), None); + + map + } + + fn get_vec_map() -> Vec<HashMap<String, Progress>> { + use Progress::*; + + let map = get_map(); + + let mut other = HashMap::new(); + other.insert(String::from("variables2"), Complete); + other.insert(String::from("functions2"), Complete); + other.insert(String::from("if1"), Complete); + other.insert(String::from("from_into"), None); + other.insert(String::from("try_from_into"), None); + + vec![map, other] + } + #[test] fn count_complete() { let map = get_map(); - assert_eq!(3, count_iterator(&map, Progress::Complete)); + assert_eq!(count_iterator(&map, Progress::Complete), 3); } #[test] fn count_some() { let map = get_map(); - assert_eq!(1, count_iterator(&map, Progress::Some)); + assert_eq!(count_iterator(&map, Progress::Some), 1); } #[test] fn count_none() { let map = get_map(); - assert_eq!(2, count_iterator(&map, Progress::None)); + assert_eq!(count_iterator(&map, Progress::None), 2); } #[test] fn count_complete_equals_for() { let map = get_map(); - let progress_states = vec![Progress::Complete, Progress::Some, Progress::None]; + let progress_states = [Progress::Complete, Progress::Some, Progress::None]; for progress_state in progress_states { assert_eq!( count_for(&map, progress_state), - count_iterator(&map, progress_state) + count_iterator(&map, progress_state), ); } } @@ -95,62 +121,33 @@ mod tests { fn count_collection_complete() { let collection = get_vec_map(); assert_eq!( + count_collection_iterator(&collection, Progress::Complete), 6, - count_collection_iterator(&collection, Progress::Complete) ); } #[test] fn count_collection_some() { let collection = get_vec_map(); - assert_eq!(1, count_collection_iterator(&collection, Progress::Some)); + assert_eq!(count_collection_iterator(&collection, Progress::Some), 1); } #[test] fn count_collection_none() { let collection = get_vec_map(); - assert_eq!(4, count_collection_iterator(&collection, Progress::None)); + assert_eq!(count_collection_iterator(&collection, Progress::None), 4); } #[test] fn count_collection_equals_for() { - let progress_states = vec![Progress::Complete, Progress::Some, Progress::None]; let collection = get_vec_map(); + let progress_states = [Progress::Complete, Progress::Some, Progress::None]; for progress_state in progress_states { assert_eq!( count_collection_for(&collection, progress_state), - count_collection_iterator(&collection, progress_state) + count_collection_iterator(&collection, progress_state), ); } } - - fn get_map() -> HashMap<String, Progress> { - use Progress::*; - - let mut map = HashMap::new(); - map.insert(String::from("variables1"), Complete); - map.insert(String::from("functions1"), Complete); - map.insert(String::from("hashmap1"), Complete); - map.insert(String::from("arc1"), Some); - map.insert(String::from("as_ref_mut"), None); - map.insert(String::from("from_str"), None); - - map - } - - fn get_vec_map() -> Vec<HashMap<String, Progress>> { - use Progress::*; - - let map = get_map(); - - let mut other = HashMap::new(); - other.insert(String::from("variables2"), Complete); - other.insert(String::from("functions2"), Complete); - other.insert(String::from("if1"), Complete); - other.insert(String::from("from_into"), None); - other.insert(String::from("try_from_into"), None); - - vec![map, other] - } } diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs index 3526ddc..6bb860f 100644 --- a/exercises/19_smart_pointers/arc1.rs +++ b/exercises/19_smart_pointers/arc1.rs @@ -1,45 +1,45 @@ -// arc1.rs +// 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. // -// 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 -// 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, โฆ // -// 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, ... +// Each thread should own a reference-counting pointer to the vector of +// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`. // -// 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. -// -// 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! -// -// Execute `rustlings hint arc1` or use the `hint` watch subcommand for a hint. +// Don't get distracted by how threads are spawned and joined. We will practice +// that later in the exercises about threads. -// I AM NOT DONE - -#![forbid(unused_imports)] // Do not change this, (or the next) line. -use std::sync::Arc; -use std::thread; +// 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 = // 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 { - let child_numbers = // TODO - joinhandles.push(thread::spawn(move || { + // TODO: Define `child_numbers` using `shared_numbers`. + // let child_numbers = ???; + + let handle = thread::spawn(move || { 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(); } } diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs index 513e7da..d70e1c3 100644 --- a/exercises/19_smart_pointers/box1.rs +++ b/exercises/19_smart_pointers/box1.rs @@ -1,45 +1,37 @@ -// box1.rs -// // At compile time, Rust needs to know how much space a type takes up. This // becomes problematic for recursive types, where a value can have as part of // itself another value of the same type. To get around the issue, we can use a // `Box` - a smart pointer used to store data on the heap, which also allows us // to wrap a recursive type. // -// The recursive type we're implementing in this exercise is the `cons list` - a +// The recursive type we're implementing in this exercise is the "cons list", a // data structure frequently found in functional programming languages. Each -// item in a cons list contains two elements: the value of the current item and +// item in a cons list contains two elements: The value of the current item and // the next item. The last item is a value called `Nil`. -// -// Step 1: use a `Box` in the enum definition to make the code compile -// Step 2: create both empty and non-empty cons lists by replacing `todo!()` -// -// Note: the tests should not be changed -// -// Execute `rustlings hint box1` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE +// TODO: Use a `Box` in the enum definition to make the code compile. #[derive(PartialEq, Debug)] -pub enum List { +enum List { Cons(i32, List), Nil, } -fn main() { - println!("This is an empty cons list: {:?}", create_empty_list()); - println!( - "This is a non-empty cons list: {:?}", - create_non_empty_list() - ); +// TODO: Create an empty cons list. +fn create_empty_list() -> List { + todo!() } -pub fn create_empty_list() -> List { +// TODO: Create a non-empty cons list. +fn create_non_empty_list() -> List { todo!() } -pub fn create_non_empty_list() -> List { - todo!() +fn main() { + println!("This is an empty cons list: {:?}", create_empty_list()); + println!( + "This is a non-empty cons list: {:?}", + create_non_empty_list(), + ); } #[cfg(test)] @@ -48,11 +40,11 @@ mod tests { #[test] fn test_create_empty_list() { - assert_eq!(List::Nil, create_empty_list()) + assert_eq!(create_empty_list(), List::Nil); } #[test] fn test_create_non_empty_list() { - assert_ne!(create_empty_list(), create_non_empty_list()) + assert_ne!(create_empty_list(), create_non_empty_list()); } } diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs index fcd3e0b..5ecf848 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -1,30 +1,22 @@ -// cow1.rs -// -// This exercise explores the Cow, or Clone-On-Write type. Cow is a -// 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. -// -// 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. -// -// Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE +// 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<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { - for i in 0..input.len() { - let v = input[i]; - if v < 0 { +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()[i] = -v; + input.to_mut()[ind] = -value; } } - input +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] @@ -32,47 +24,45 @@ mod tests { use super::*; #[test] - fn reference_mutation() -> Result<(), &'static str> { + fn reference_mutation() { // Clone occurs because `input` needs to be mutated. - let slice = [-1, 0, 1]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - Cow::Owned(_) => Ok(()), - _ => Err("Expected owned value"), - } + 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() -> Result<(), &'static str> { + fn reference_no_mutation() { // No clone occurs because `input` doesn't need to be mutated. - let slice = [0, 1, 2]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - // TODO - } + let vec = vec![0, 1, 2]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } #[test] - fn owned_no_mutation() -> Result<(), &'static str> { - // We can also pass `slice` without `&` so Cow owns it directly. In this - // case no mutation occurs and thus also no clone, but the result is + 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 slice = vec![0, 1, 2]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - } + let vec = vec![0, 1, 2]; + let mut input = Cow::from(vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } #[test] - fn owned_mutation() -> Result<(), &'static str> { + 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 + // case, the call to `to_mut()` in the `abs_all` function returns a // reference to the same data as before. - let slice = vec![-1, 0, 1]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - } + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } } diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index 1b90346..48e19dc 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -1,22 +1,14 @@ -// rc1.rs -// // 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 Planets. The Planets take ownership of the sun, indicating that they -// revolve around the sun. -// -// Make this code compile by using the proper Rc primitives to express that the -// sun has multiple owners. -// -// Execute `rustlings hint rc1` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE +// `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 {} +struct Sun; +#[allow(dead_code)] #[derive(Debug)] enum Planet { Mercury(Rc<Sun>), @@ -31,75 +23,84 @@ enum Planet { impl Planet { fn details(&self) { - println!("Hi from {:?}!", self) + println!("Hi from {self:?}!"); } } -#[test] fn main() { - let sun = Rc::new(Sun {}); - println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; - let mercury = Planet::Mercury(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 2 references - mercury.details(); + #[test] + fn rc1() { + let sun = Rc::new(Sun); + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference - let venus = Planet::Venus(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 3 references - venus.details(); + let mercury = Planet::Mercury(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + mercury.details(); - let earth = Planet::Earth(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 4 references - earth.details(); + let venus = Planet::Venus(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + venus.details(); - let mars = Planet::Mars(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 5 references - mars.details(); + let earth = Planet::Earth(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + earth.details(); - let jupiter = Planet::Jupiter(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 6 references - jupiter.details(); + let mars = Planet::Mars(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + mars.details(); - // TODO - let saturn = Planet::Saturn(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 7 references - saturn.details(); + let jupiter = Planet::Jupiter(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + jupiter.details(); - // TODO - let uranus = Planet::Uranus(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 8 references - uranus.details(); + // TODO + let saturn = Planet::Saturn(Rc::new(Sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + saturn.details(); - // TODO - let neptune = Planet::Neptune(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 9 references - neptune.details(); + // TODO + let uranus = Planet::Uranus(Rc::new(Sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + uranus.details(); - assert_eq!(Rc::strong_count(&sun), 9); + // TODO + let neptune = Planet::Neptune(Rc::new(Sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 9 references + neptune.details(); - drop(neptune); - println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + assert_eq!(Rc::strong_count(&sun), 9); - drop(uranus); - println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + drop(neptune); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references - drop(saturn); - println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + drop(uranus); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references - drop(jupiter); - println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + drop(saturn); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references - drop(mars); - println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + drop(jupiter); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + drop(mars); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references - assert_eq!(Rc::strong_count(&sun), 1); + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + assert_eq!(Rc::strong_count(&sun), 1); + } } diff --git a/exercises/20_threads/README.md b/exercises/20_threads/README.md index dbe6664..0b32fb1 100644 --- a/exercises/20_threads/README.md +++ b/exercises/20_threads/README.md @@ -7,3 +7,4 @@ Within your program, you can also have independent parts that run simultaneously - [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html) - [Using Threads to Run Code Simultaneously](https://doc.rust-lang.org/book/ch16-01-threads.html) +- [Using Message Passing to Transfer Data Between Threads](https://doc.rust-lang.org/book/ch16-02-message-passing.html) diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs index 80b6def..01f9ff4 100644 --- a/exercises/20_threads/threads1.rs +++ b/exercises/20_threads/threads1.rs @@ -1,40 +1,37 @@ -// threads1.rs -// // This program spawns multiple threads that each run for at least 250ms, and // each thread returns how much time they took to complete. The program should // wait until all the spawned threads have finished and should collect their // return values into a vector. -// -// Execute `rustlings hint threads1` or use the `hint` watch subcommand for a -// hint. -// I AM NOT DONE - -use std::thread; -use std::time::{Duration, Instant}; +use std::{ + thread, + time::{Duration, Instant}, +}; fn main() { - let mut handles = vec![]; + let mut handles = Vec::new(); for i in 0..10 { - handles.push(thread::spawn(move || { + let handle = thread::spawn(move || { let start = Instant::now(); thread::sleep(Duration::from_millis(250)); - println!("thread {} is complete", i); + println!("Thread {i} done"); start.elapsed().as_millis() - })); + }); + handles.push(handle); } - let mut results: Vec<u128> = vec![]; + let mut results = Vec::new(); for handle in handles { - // TODO: a struct is returned from thread::spawn, can you use it? + // TODO: Collect the results of all threads into the `results` vector. + // Use the `JoinHandle` struct which is returned by `thread::spawn`. } if results.len() != 10 { - panic!("Oh no! All the spawned threads did not finish!"); + panic!("Oh no! Some thread isn't done yet!"); } println!(); for (i, result) in results.into_iter().enumerate() { - println!("thread {} took {}ms", i, result); + println!("Thread {i} took {result}ms"); } } diff --git a/exercises/20_threads/threads2.rs b/exercises/20_threads/threads2.rs index 62dad80..7020cb9 100644 --- a/exercises/20_threads/threads2.rs +++ b/exercises/20_threads/threads2.rs @@ -1,39 +1,34 @@ -// threads2.rs -// // Building on the last exercise, we want all of the threads to complete their -// work but this time the spawned threads need to be in charge of updating a -// shared value: JobStatus.jobs_completed -// -// Execute `rustlings hint threads2` or use the `hint` watch subcommand for a -// hint. +// work. But this time, the spawned threads need to be in charge of updating a +// shared value: `JobStatus.jobs_done` -// I AM NOT DONE - -use std::sync::Arc; -use std::thread; -use std::time::Duration; +use std::{sync::Arc, thread, time::Duration}; struct JobStatus { - jobs_completed: u32, + jobs_done: u32, } fn main() { - let status = Arc::new(JobStatus { jobs_completed: 0 }); - let mut handles = vec![]; + // TODO: `Arc` isn't enough if you want a **mutable** shared state. + let status = Arc::new(JobStatus { jobs_done: 0 }); + + let mut handles = Vec::new(); for _ in 0..10 { let status_shared = Arc::clone(&status); let handle = thread::spawn(move || { thread::sleep(Duration::from_millis(250)); - // TODO: You must take an action before you update a shared value - status_shared.jobs_completed += 1; + + // TODO: You must take an action before you update a shared value. + status_shared.jobs_done += 1; }); handles.push(handle); } + + // Waiting for all jobs to complete. for handle in handles { handle.join().unwrap(); - // TODO: Print the value of the JobStatus.jobs_completed. Did you notice - // anything interesting in the output? Do you have to 'join' on all the - // handles? - println!("jobs completed {}", ???); } + + // TODO: Print the value of `JobStatus.jobs_done`. + println!("Jobs done: {}", todo!()); } diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index 91006bb..8aa7291 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -1,14 +1,4 @@ -// threads3.rs -// -// Execute `rustlings hint threads3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -use std::sync::mpsc; -use std::sync::Arc; -use std::thread; -use std::time::Duration; +use std::{sync::mpsc, thread, time::Duration}; struct Queue { length: u32, @@ -18,7 +8,7 @@ struct Queue { impl Queue { fn new() -> Self { - Queue { + Self { length: 10, first_half: vec![1, 2, 3, 4, 5], second_half: vec![6, 7, 8, 9, 10], @@ -26,42 +16,49 @@ impl Queue { } } -fn send_tx(q: Queue, tx: mpsc::Sender<u32>) -> () { - let qc = Arc::new(q); - let qc1 = Arc::clone(&qc); - let qc2 = Arc::clone(&qc); - +fn send_tx(q: Queue, tx: mpsc::Sender<u32>) { + // TODO: We want to send `tx` to both threads. But currently, it is moved + // into the first thread. How could you solve this problem? thread::spawn(move || { - for val in &qc1.first_half { - println!("sending {:?}", val); - tx.send(*val).unwrap(); - thread::sleep(Duration::from_secs(1)); + for val in q.first_half { + println!("Sending {val:?}"); + tx.send(val).unwrap(); + thread::sleep(Duration::from_millis(250)); } }); thread::spawn(move || { - for val in &qc2.second_half { - println!("sending {:?}", val); - tx.send(*val).unwrap(); - thread::sleep(Duration::from_secs(1)); + for val in q.second_half { + println!("Sending {val:?}"); + tx.send(val).unwrap(); + thread::sleep(Duration::from_millis(250)); } }); } -#[test] fn main() { - let (tx, rx) = mpsc::channel(); - let queue = Queue::new(); - let queue_length = queue.length; + // You can optionally experiment here. +} - send_tx(queue, tx); +#[cfg(test)] +mod tests { + use super::*; - let mut total_received: u32 = 0; - for received in rx { - println!("Got: {}", received); - total_received += 1; - } + #[test] + fn threads3() { + let (tx, rx) = mpsc::channel(); + let queue = Queue::new(); + let queue_length = queue.length; - println!("total numbers received: {}", total_received); - assert_eq!(total_received, queue_length) + send_tx(queue, tx); + + let mut total_received: u32 = 0; + for received in rx { + println!("Got: {received}"); + total_received += 1; + } + + println!("Number of received values: {total_received}"); + assert_eq!(total_received, queue_length); + } } diff --git a/exercises/21_macros/macros1.rs b/exercises/21_macros/macros1.rs index 678de6e..fb3c3ff 100644 --- a/exercises/21_macros/macros1.rs +++ b/exercises/21_macros/macros1.rs @@ -1,10 +1,3 @@ -// macros1.rs -// -// Execute `rustlings hint macros1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - macro_rules! my_macro { () => { println!("Check out my macro!"); @@ -12,5 +5,6 @@ macro_rules! my_macro { } fn main() { + // TODO: Fix the macro call. my_macro(); } diff --git a/exercises/21_macros/macros2.rs b/exercises/21_macros/macros2.rs index 788fc16..2d9dec7 100644 --- a/exercises/21_macros/macros2.rs +++ b/exercises/21_macros/macros2.rs @@ -1,14 +1,8 @@ -// macros2.rs -// -// Execute `rustlings hint macros2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - fn main() { my_macro!(); } +// TODO: Fix the compiler error by moving the whole definition of this macro. macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/exercises/21_macros/macros3.rs b/exercises/21_macros/macros3.rs index b795c14..9537494 100644 --- a/exercises/21_macros/macros3.rs +++ b/exercises/21_macros/macros3.rs @@ -1,12 +1,5 @@ -// macros3.rs -// -// Make me compile, without taking the macro out of the module! -// -// Execute `rustlings hint macros3` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +// TODO: Fix the compiler error without taking the macro definition out of this +// module. mod macros { macro_rules! my_macro { () => { diff --git a/exercises/21_macros/macros4.rs b/exercises/21_macros/macros4.rs index 71b45a0..9d77f6a 100644 --- a/exercises/21_macros/macros4.rs +++ b/exercises/21_macros/macros4.rs @@ -1,10 +1,4 @@ -// macros4.rs -// -// Execute `rustlings hint macros4` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - +// TODO: Fix the compiler error by adding one or two characters. #[rustfmt::skip] macro_rules! my_macro { () => { diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs index 95c0141..7165da4 100644 --- a/exercises/22_clippy/clippy1.rs +++ b/exercises/22_clippy/clippy1.rs @@ -1,26 +1,15 @@ -// clippy1.rs -// // The Clippy tool is a collection of lints to analyze your code so you can // catch common mistakes and improve your Rust code. // -// For these exercises the code will fail to compile when there are clippy -// warnings check clippy's suggestions from the output to solve the exercise. -// -// Execute `rustlings hint clippy1` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - -use std::f32; +// For these exercises, the code will fail to compile when there are Clippy +// warnings. Check Clippy's suggestions from the output to solve the exercise. fn main() { - let pi = 3.14f32; - let radius = 5.00f32; + // TODO: Fix the Clippy lint in this line. + let pi = 3.14; + let radius: f32 = 5.0; - let area = pi * f32::powi(radius, 2); + let area = pi * radius.powi(2); - println!( - "The area of a circle with radius {:.2} is {:.5}!", - radius, area - ) + println!("The area of a circle with radius {radius:.2} is {area:.5}"); } diff --git a/exercises/22_clippy/clippy2.rs b/exercises/22_clippy/clippy2.rs index 9b87a0b..8cfe6f8 100644 --- a/exercises/22_clippy/clippy2.rs +++ b/exercises/22_clippy/clippy2.rs @@ -1,15 +1,10 @@ -// clippy2.rs -// -// Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE - fn main() { let mut res = 42; let option = Some(12); + // TODO: Fix the Clippy lint. for x in option { res += x; } - println!("{}", res); + + println!("{res}"); } diff --git a/exercises/22_clippy/clippy3.rs b/exercises/22_clippy/clippy3.rs index 5a95f5b..4f78834 100644 --- a/exercises/22_clippy/clippy3.rs +++ b/exercises/22_clippy/clippy3.rs @@ -1,30 +1,27 @@ -// clippy3.rs -// -// Here's a couple more easy Clippy fixes, so you can see its utility. -// No hints. - -// I AM NOT DONE +// Here are some more easy Clippy fixes so you can see its utility ๐ +// TODO: Fix all the Clippy lints. +#[rustfmt::skip] #[allow(unused_variables, unused_assignments)] fn main() { let my_option: Option<()> = None; if my_option.is_none() { - my_option.unwrap(); + println!("{:?}", my_option.unwrap()); } let my_arr = &[ -1, -2, -3 -4, -5, -6 ]; - println!("My array! Here it is: {:?}", my_arr); + println!("My array! Here it is: {my_arr:?}"); let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5); - println!("This Vec is empty, see? {:?}", my_empty_vec); + println!("This Vec is empty, see? {my_empty_vec:?}"); let mut value_a = 45; let mut value_b = 66; // Let's swap these two! value_a = value_b; value_b = value_a; - println!("value a: {}; value b: {}", value_a, value_b); + println!("value a: {value_a}; value b: {value_b}"); } diff --git a/exercises/23_conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs index 2ba9e3f..54f0cd1 100644 --- a/exercises/23_conversions/as_ref_mut.rs +++ b/exercises/23_conversions/as_ref_mut.rs @@ -1,31 +1,27 @@ -// as_ref_mut.rs -// // AsRef and AsMut allow for cheap reference-to-reference conversions. Read more // about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and // https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively. -// -// Execute `rustlings hint as_ref_mut` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE // Obtain the number of bytes (not characters) in the given argument. -// TODO: Add the AsRef trait appropriately as a trait bound. +// TODO: Add the `AsRef` trait appropriately as a trait bound. fn byte_counter<T>(arg: T) -> usize { arg.as_ref().as_bytes().len() } // Obtain the number of characters (not bytes) in the given argument. -// TODO: Add the AsRef trait appropriately as a trait bound. +// TODO: Add the `AsRef` trait appropriately as a trait bound. fn char_counter<T>(arg: T) -> usize { arg.as_ref().chars().count() } -// Squares a number using as_mut(). +// Squares a number using `as_mut()`. // TODO: Add the appropriate trait bound. fn num_sq<T>(arg: &mut T) { // TODO: Implement the function body. - ??? +} + +fn main() { + // You can optionally experiment here. } #[cfg(test)] diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 60911f3..bc2783a 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -1,89 +1,79 @@ -// from_into.rs -// -// The From trait is used for value-to-value conversions. If From is implemented -// correctly for a type, the Into trait should work conversely. You can read -// more about it at https://doc.rust-lang.org/std/convert/trait.From.html -// -// Execute `rustlings hint from_into` or use the `hint` watch subcommand for a -// hint. +// The `From` trait is used for value-to-value conversions. If `From` is +// implemented, an implementation of `Into` is automatically provided. +// You can read more about it in the documentation: +// https://doc.rust-lang.org/std/convert/trait.From.html #[derive(Debug)] struct Person { name: String, - age: usize, + age: u8, } -// We implement the Default trait to use it as a fallback -// when the provided string is not convertible into a Person object +// We implement the Default trait to use it as a fallback when the provided +// string is not convertible into a `Person` object. impl Default for Person { - fn default() -> Person { - Person { + fn default() -> Self { + Self { name: String::from("John"), age: 30, } } } -// Your task is to complete this implementation in order for the line `let p = -// Person::from("Mark,20")` to compile Please note that you'll need to parse the -// age component into a `usize` with something like `"4".parse::<usize>()`. The -// outcome of this needs to be handled appropriately. +// TODO: Complete this `From` implementation to be able to parse a `Person` +// out of a string in the form of "Mark,20". +// Note that you'll need to parse the age component into a `u8` with something +// like `"4".parse::<u8>()`. // // Steps: -// 1. If the length of the provided string is 0, then return the default of -// Person. -// 2. Split the given string on the commas present in it. -// 3. Extract the first element from the split operation and use it as the name. -// 4. If the name is empty, then return the default of Person. -// 5. Extract the other element from the split operation and parse it into a -// `usize` as the age. -// If while parsing the age, something goes wrong, then return the default of -// Person Otherwise, then return an instantiated Person object with the results - -// I AM NOT DONE - +// 1. Split the given string on the commas present in it. +// 2. If the split operation returns less or more than 2 elements, return the +// default of `Person`. +// 3. Use the first element from the split operation as the name. +// 4. If the name is empty, return the default of `Person`. +// 5. Parse the second element from the split operation into a `u8` as the age. +// 6. If parsing the age fails, return the default of `Person`. impl From<&str> for Person { - fn from(s: &str) -> Person { - } + fn from(s: &str) -> Self {} } fn main() { - // Use the `from` function + // Use the `from` function. let p1 = Person::from("Mark,20"); - // Since From is implemented for Person, we should be able to use Into + println!("{p1:?}"); + + // Since `From` is implemented for Person, we are able to use `Into`. let p2: Person = "Gerald,70".into(); - println!("{:?}", p1); - println!("{:?}", p2); + println!("{p2:?}"); } #[cfg(test)] mod tests { use super::*; + #[test] fn test_default() { - // Test that the default person is 30 year old John let dp = Person::default(); assert_eq!(dp.name, "John"); assert_eq!(dp.age, 30); } + #[test] fn test_bad_convert() { - // Test that John is returned when bad string is provided let p = Person::from(""); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } + #[test] fn test_good_convert() { - // Test that "Mark,20" works let p = Person::from("Mark,20"); assert_eq!(p.name, "Mark"); assert_eq!(p.age, 20); } + #[test] fn test_bad_age() { - // Test that "Mark,twenty" will return the default person due to an - // error in parsing age let p = Person::from("Mark,twenty"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); @@ -127,14 +117,14 @@ mod tests { #[test] fn test_trailing_comma() { let p: Person = Person::from("Mike,32,"); - assert_eq!(p.name, "Mike"); - assert_eq!(p.age, 32); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); } #[test] fn test_trailing_comma_and_some_string() { - let p: Person = Person::from("Mike,32,man"); - assert_eq!(p.name, "Mike"); - assert_eq!(p.age, 32); + let p: Person = Person::from("Mike,32,dog"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); } } diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index 34472c3..4b1aaa2 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -1,13 +1,9 @@ -// from_str.rs -// -// This is similar to from_into.rs, but this time we'll implement `FromStr` and -// return errors instead of falling back to a default value. Additionally, upon -// implementing FromStr, you can use the `parse` method on strings to generate -// an object of the implementor type. You can read more about it at +// This is similar to the previous `from_into` exercise. But this time, we'll +// implement `FromStr` and return errors instead of falling back to a default +// value. Additionally, upon implementing `FromStr`, you can use the `parse` +// method on strings to generate an object of the implementor type. You can read +// more about it in the documentation: // https://doc.rust-lang.org/std/str/trait.FromStr.html -// -// Execute `rustlings hint from_str` or use the `hint` watch subcommand for a -// hint. use std::num::ParseIntError; use std::str::FromStr; @@ -15,59 +11,54 @@ use std::str::FromStr; #[derive(Debug, PartialEq)] struct Person { name: String, - age: usize, + age: u8, } // We will use this error type for the `FromStr` implementation. #[derive(Debug, PartialEq)] enum ParsePersonError { - // Empty input string - Empty, // Incorrect number of fields BadLen, // Empty name field NoName, - // Wrapped error from parse::<usize>() + // Wrapped error from parse::<u8>() ParseInt(ParseIntError), } -// I AM NOT DONE - -// Steps: -// 1. If the length of the provided string is 0, an error should be returned -// 2. Split the given string on the commas present in it -// 3. Only 2 elements should be returned from the split, otherwise return an -// error -// 4. Extract the first element from the split operation and use it as the name -// 5. Extract the other element from the split operation and parse it into a -// `usize` as the age with something like `"4".parse::<usize>()` -// 6. If while extracting the name and the age something goes wrong, an error -// should be returned -// If everything goes well, then return a Result of a Person object +// TODO: Complete this `From` implementation to be able to parse a `Person` +// out of a string in the form of "Mark,20". +// Note that you'll need to parse the age component into a `u8` with something +// like `"4".parse::<u8>()`. // -// As an aside: `Box<dyn Error>` implements `From<&'_ str>`. This means that if -// you want to return a string error message, you can do so via just using -// return `Err("my error message".into())`. - +// Steps: +// 1. Split the given string on the commas present in it. +// 2. If the split operation returns less or more than 2 elements, return the +// error `ParsePersonError::BadLen`. +// 3. Use the first element from the split operation as the name. +// 4. If the name is empty, return the error `ParsePersonError::NoName`. +// 5. Parse the second element from the split operation into a `u8` as the age. +// 6. If parsing the age fails, return the error `ParsePersonError::ParseInt`. impl FromStr for Person { type Err = ParsePersonError; - fn from_str(s: &str) -> Result<Person, Self::Err> { - } + + fn from_str(s: &str) -> Result<Self, Self::Err> {} } fn main() { - let p = "Mark,20".parse::<Person>().unwrap(); - println!("{:?}", p); + let p = "Mark,20".parse::<Person>(); + println!("{p:?}"); } #[cfg(test)] mod tests { use super::*; + use ParsePersonError::*; #[test] fn empty_input() { - assert_eq!("".parse::<Person>(), Err(ParsePersonError::Empty)); + assert_eq!("".parse::<Person>(), Err(BadLen)); } + #[test] fn good_input() { let p = "John,32".parse::<Person>(); @@ -76,58 +67,47 @@ mod tests { assert_eq!(p.name, "John"); assert_eq!(p.age, 32); } + #[test] fn missing_age() { - assert!(matches!( - "John,".parse::<Person>(), - Err(ParsePersonError::ParseInt(_)) - )); + assert!(matches!("John,".parse::<Person>(), Err(ParseInt(_)))); } #[test] fn invalid_age() { - assert!(matches!( - "John,twenty".parse::<Person>(), - Err(ParsePersonError::ParseInt(_)) - )); + assert!(matches!("John,twenty".parse::<Person>(), Err(ParseInt(_)))); } #[test] fn missing_comma_and_age() { - assert_eq!("John".parse::<Person>(), Err(ParsePersonError::BadLen)); + assert_eq!("John".parse::<Person>(), Err(BadLen)); } #[test] fn missing_name() { - assert_eq!(",1".parse::<Person>(), Err(ParsePersonError::NoName)); + assert_eq!(",1".parse::<Person>(), Err(NoName)); } #[test] fn missing_name_and_age() { - assert!(matches!( - ",".parse::<Person>(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)) - )); + assert!(matches!(",".parse::<Person>(), Err(NoName | ParseInt(_)))); } #[test] fn missing_name_and_invalid_age() { assert!(matches!( ",one".parse::<Person>(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)) + Err(NoName | ParseInt(_)), )); } #[test] fn trailing_comma() { - assert_eq!("John,32,".parse::<Person>(), Err(ParsePersonError::BadLen)); + assert_eq!("John,32,".parse::<Person>(), Err(BadLen)); } #[test] fn trailing_comma_and_some_string() { - assert_eq!( - "John,32,man".parse::<Person>(), - Err(ParsePersonError::BadLen) - ); + assert_eq!("John,32,man".parse::<Person>(), Err(BadLen)); } } diff --git a/exercises/23_conversions/try_from_into.rs b/exercises/23_conversions/try_from_into.rs index 32d6ef3..f3ae80a 100644 --- a/exercises/23_conversions/try_from_into.rs +++ b/exercises/23_conversions/try_from_into.rs @@ -1,14 +1,10 @@ -// try_from_into.rs -// -// TryFrom is a simple and safe type conversion that may fail in a controlled -// way under some circumstances. Basically, this is the same as From. The main -// difference is that this should return a Result type instead of the target -// type itself. You can read more about it at +// `TryFrom` is a simple and safe type conversion that may fail in a controlled +// way under some circumstances. Basically, this is the same as `From`. The main +// difference is that this should return a `Result` type instead of the target +// type itself. You can read more about it in the documentation: // https://doc.rust-lang.org/std/convert/trait.TryFrom.html -// -// Execute `rustlings hint try_from_into` or use the `hint` watch subcommand for -// a hint. +#![allow(clippy::useless_vec)] use std::convert::{TryFrom, TryInto}; #[derive(Debug, PartialEq)] @@ -18,7 +14,7 @@ struct Color { blue: u8, } -// We will use this error type for these `TryFrom` conversions. +// We will use this error type for the `TryFrom` conversions. #[derive(Debug, PartialEq)] enum IntoColorError { // Incorrect length of slice @@ -27,80 +23,67 @@ enum IntoColorError { IntConversion, } -// I AM NOT DONE - -// Your task is to complete this implementation and return an Ok result of inner -// type Color. You need to create an implementation for a tuple of three -// integers, an array of three integers, and a slice of integers. -// -// Note that the implementation for tuple and array will be checked at compile -// time, but the slice implementation needs to check the slice length! Also note -// that correct RGB color values must be integers in the 0..=255 range. - -// Tuple implementation +// TODO: Tuple implementation. +// Correct RGB color values must be integers in the 0..=255 range. impl TryFrom<(i16, i16, i16)> for Color { type Error = IntoColorError; - fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> { - } + + fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {} } -// Array implementation +// TODO: Array implementation. impl TryFrom<[i16; 3]> for Color { type Error = IntoColorError; - fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> { - } + + fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {} } -// Slice implementation +// TODO: Slice implementation. +// This implementation needs to check the slice length. impl TryFrom<&[i16]> for Color { type Error = IntoColorError; - fn try_from(slice: &[i16]) -> Result<Self, Self::Error> { - } + + fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {} } fn main() { - // Use the `try_from` function + // Using the `try_from` function. let c1 = Color::try_from((183, 65, 14)); - println!("{:?}", c1); + println!("{c1:?}"); - // Since TryFrom is implemented for Color, we should be able to use TryInto + // Since `TryFrom` is implemented for `Color`, we can use `TryInto`. let c2: Result<Color, _> = [183, 65, 14].try_into(); - println!("{:?}", c2); + println!("{c2:?}"); let v = vec![183, 65, 14]; - // With slice we should use `try_from` function + // With slice we should use the `try_from` function let c3 = Color::try_from(&v[..]); - println!("{:?}", c3); - // or take slice within round brackets and use TryInto + println!("{c3:?}"); + // or put the slice within round brackets and use `try_into`. let c4: Result<Color, _> = (&v[..]).try_into(); - println!("{:?}", c4); + println!("{c4:?}"); } #[cfg(test)] mod tests { use super::*; + use IntoColorError::*; #[test] fn test_tuple_out_of_range_positive() { - assert_eq!( - Color::try_from((256, 1000, 10000)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion)); } + #[test] fn test_tuple_out_of_range_negative() { - assert_eq!( - Color::try_from((-1, -10, -256)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion)); } + #[test] fn test_tuple_sum() { - assert_eq!( - Color::try_from((-1, 255, 255)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion)); } + #[test] fn test_tuple_correct() { let c: Result<Color, _> = (183, 65, 14).try_into(); @@ -110,25 +93,29 @@ mod tests { Color { red: 183, green: 65, - blue: 14 + blue: 14, } ); } + #[test] fn test_array_out_of_range_positive() { let c: Result<Color, _> = [1000, 10000, 256].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_out_of_range_negative() { let c: Result<Color, _> = [-10, -256, -1].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_sum() { let c: Result<Color, _> = [-1, 255, 255].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_correct() { let c: Result<Color, _> = [183, 65, 14].try_into(); @@ -142,30 +129,25 @@ mod tests { } ); } + #[test] fn test_slice_out_of_range_positive() { let arr = [10000, 256, 1000]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_out_of_range_negative() { let arr = [-256, -1, -10]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_sum() { let arr = [-1, 255, 255]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_correct() { let v = vec![183, 65, 14]; @@ -176,18 +158,20 @@ mod tests { Color { red: 183, green: 65, - blue: 14 + blue: 14, } ); } + #[test] fn test_slice_excess_length() { let v = vec![0, 0, 0, 0]; - assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen)); + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); } + #[test] fn test_slice_insufficient_length() { let v = vec![0, 0]; - assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen)); + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); } } diff --git a/exercises/23_conversions/using_as.rs b/exercises/23_conversions/using_as.rs index 414cef3..c131d1f 100644 --- a/exercises/23_conversions/using_as.rs +++ b/exercises/23_conversions/using_as.rs @@ -1,19 +1,10 @@ -// using_as.rs -// -// Type casting in Rust is done via the usage of the `as` operator. Please note -// that the `as` operator is not only used when type casting. It also helps with -// renaming imports. -// -// The goal is to make sure that the division does not fail to compile and -// returns the proper type. -// -// Execute `rustlings hint using_as` or use the `hint` watch subcommand for a -// hint. - -// I AM NOT DONE +// Type casting in Rust is done via the usage of the `as` operator. +// Note that the `as` operator is not only used when type casting. It also helps +// with renaming imports. fn average(values: &[f64]) -> f64 { let total = values.iter().sum::<f64>(); + // TODO: Make a conversion before dividing. total / values.len() } diff --git a/exercises/README.md b/exercises/README.md index c7effa9..237f2f1 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -17,11 +17,11 @@ | error_handling | ยง9 | | generics | ยง10 | | traits | ยง10.2 | -| tests | ยง11.1 | | lifetimes | ยง10.3 | +| tests | ยง11.1 | | iterators | ยง13.2-4 | -| threads | ยง16.1-3 | | smart_pointers | ยง15, ยง16.3 | -| macros | ยง19.6 | +| threads | ยง16.1-3 | +| macros | ยง19.5 | | clippy | ยง21.4 | | conversions | n/a | diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs deleted file mode 100644 index 4ee5ada..0000000 --- a/exercises/quiz1.rs +++ /dev/null @@ -1,33 +0,0 @@ -// quiz1.rs -// -// This is a quiz for the following sections: -// - Variables -// - Functions -// - If -// -// Mary is buying apples. The price of an apple is calculated as follows: -// - An apple costs 2 rustbucks. -// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck! -// Write a function that calculates the price of an order of apples given the -// quantity bought. -// -// No hints this time ;) - -// I AM NOT DONE - -// Put your function here! -// fn calculate_price_of_apples { - -// Don't modify this function! -#[test] -fn verify_test() { - let price1 = calculate_price_of_apples(35); - let price2 = calculate_price_of_apples(40); - let price3 = calculate_price_of_apples(41); - let price4 = calculate_price_of_apples(65); - - assert_eq!(70, price1); - assert_eq!(80, price2); - assert_eq!(41, price3); - assert_eq!(65, price4); -} diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs deleted file mode 100644 index 29925ca..0000000 --- a/exercises/quiz2.rs +++ /dev/null @@ -1,64 +0,0 @@ -// quiz2.rs -// -// This is a quiz for the following sections: -// - Strings -// - Vecs -// - Move semantics -// - Modules -// - Enums -// -// Let's build a little machine in the form of a function. As input, we're going -// to give a list of strings and commands. These commands determine what action -// is going to be applied to the string. It can either be: -// - Uppercase the string -// - Trim the string -// - Append "bar" to the string a specified amount of times -// The exact form of this will be: -// - The input is going to be a Vector of a 2-length tuple, -// the first element is the string, the second one is the command. -// - The output element is going to be a Vector of strings. -// -// No hints this time! - -// I AM NOT DONE - -pub enum Command { - Uppercase, - Trim, - Append(usize), -} - -mod my_module { - use super::Command; - - // TODO: Complete the function signature! - pub fn transformer(input: ???) -> ??? { - // TODO: Complete the output declaration! - let mut output: ??? = vec![]; - for (string, command) in input.iter() { - // TODO: Complete the function body. You can do it! - } - output - } -} - -#[cfg(test)] -mod tests { - // TODO: What do we need to import to have `transformer` in scope? - use ???; - use super::Command; - - #[test] - fn it_works() { - let output = transformer(vec![ - ("hello".into(), Command::Uppercase), - (" all roads lead to rome! ".into(), Command::Trim), - ("foo".into(), Command::Append(1)), - ("bar".into(), Command::Append(5)), - ]); - assert_eq!(output[0], "HELLO"); - assert_eq!(output[1], "all roads lead to rome!"); - assert_eq!(output[2], "foobar"); - assert_eq!(output[3], "barbarbarbarbarbar"); - } -} diff --git a/exercises/quizzes/README.md b/exercises/quizzes/README.md new file mode 100644 index 0000000..4d3bcd9 --- /dev/null +++ b/exercises/quizzes/README.md @@ -0,0 +1,3 @@ +# Quizzes + +After every couple of sections, there will be a quiz in this directory that'll test your knowledge on a bunch of sections at once. diff --git a/exercises/quizzes/quiz1.rs b/exercises/quizzes/quiz1.rs new file mode 100644 index 0000000..afbf1f9 --- /dev/null +++ b/exercises/quizzes/quiz1.rs @@ -0,0 +1,31 @@ +// This is a quiz for the following sections: +// - Variables +// - Functions +// - If +// +// Mary is buying apples. The price of an apple is calculated as follows: +// - An apple costs 2 rustbucks. +// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck! +// TODO: Write a function that calculates the price of an order of apples given +// the quantity bought. + +// Put your function here! +// fn calculate_price_of_apples(???) -> ??? { + +fn main() { + // You can optionally experiment here. +} + +// Don't change the tests! +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_test() { + assert_eq!(calculate_price_of_apples(35), 70); + assert_eq!(calculate_price_of_apples(40), 80); + assert_eq!(calculate_price_of_apples(41), 41); + assert_eq!(calculate_price_of_apples(65), 65); + } +} diff --git a/exercises/quizzes/quiz2.rs b/exercises/quizzes/quiz2.rs new file mode 100644 index 0000000..8ef1342 --- /dev/null +++ b/exercises/quizzes/quiz2.rs @@ -0,0 +1,63 @@ +// This is a quiz for the following sections: +// - Strings +// - Vecs +// - Move semantics +// - Modules +// - Enums +// +// Let's build a little machine in the form of a function. As input, we're going +// to give a list of strings and commands. These commands determine what action +// is going to be applied to the string. It can either be: +// - Uppercase the string +// - Trim the string +// - Append "bar" to the string a specified amount of times +// +// The exact form of this will be: +// - The input is going to be a Vector of 2-length tuples, +// the first element is the string, the second one is the command. +// - The output element is going to be a vector of strings. + +enum Command { + Uppercase, + Trim, + Append(usize), +} + +mod my_module { + use super::Command; + + // TODO: Complete the function. + // pub fn transformer(input: ???) -> ??? { ??? } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + // TODO: What do we need to import to have `transformer` in scope? + // use ???; + use super::Command; + + #[test] + fn it_works() { + let input = vec![ + ("hello".to_string(), Command::Uppercase), + (" all roads lead to rome! ".to_string(), Command::Trim), + ("foo".to_string(), Command::Append(1)), + ("bar".to_string(), Command::Append(5)), + ]; + let output = transformer(input); + + assert_eq!( + output, + [ + "HELLO", + "all roads lead to rome!", + "foobar", + "barbarbarbarbarbar", + ] + ); + } +} diff --git a/exercises/quiz3.rs b/exercises/quizzes/quiz3.rs index 3b01d31..c877c5f 100644 --- a/exercises/quiz3.rs +++ b/exercises/quizzes/quiz3.rs @@ -1,36 +1,37 @@ -// quiz3.rs -// // This quiz tests: // - Generics // - Traits // // An imaginary magical school has a new report card generation system written -// in Rust! Currently the system only supports creating report cards where the +// in Rust! Currently, the system only supports creating report cards where the // student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the // school also issues alphabetical grades (A+ -> F-) and needs to be able to // print both types of report card! // -// Make the necessary code changes in the struct ReportCard and the impl block -// to support alphabetical report cards. Change the Grade in the second test to -// "A+" to show that your changes allow alphabetical grades. -// -// Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint. - -// I AM NOT DONE +// Make the necessary code changes in the struct `ReportCard` and the impl +// block to support alphabetical report cards in addition to numerical ones. -pub struct ReportCard { - pub grade: f32, - pub student_name: String, - pub student_age: u8, +// TODO: Adjust the struct as described above. +struct ReportCard { + grade: f32, + student_name: String, + student_age: u8, } +// TODO: Adjust the impl block as described above. impl ReportCard { - pub fn print(&self) -> String { - format!("{} ({}) - achieved a grade of {}", - &self.student_name, &self.student_age, &self.grade) + fn print(&self) -> String { + format!( + "{} ({}) - achieved a grade of {}", + &self.student_name, &self.student_age, &self.grade, + ) } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -44,21 +45,20 @@ mod tests { }; assert_eq!( report_card.print(), - "Tom Wriggle (12) - achieved a grade of 2.1" + "Tom Wriggle (12) - achieved a grade of 2.1", ); } #[test] fn generate_alphabetic_report_card() { - // TODO: Make sure to change the grade here after you finish the exercise. let report_card = ReportCard { - grade: 2.1, + grade: "A+", student_name: "Gary Plotter".to_string(), student_age: 11, }; assert_eq!( report_card.print(), - "Gary Plotter (11) - achieved a grade of A+" + "Gary Plotter (11) - achieved a grade of A+", ); } } |
