diff --git a/.rustlings-state.txt b/.rustlings-state.txt index c3678cb2..56f1a680 100644 --- a/.rustlings-state.txt +++ b/.rustlings-state.txt @@ -1,6 +1,6 @@ DON'T EDIT THIS FILE! -generics1 +box1 intro1 intro2 @@ -57,4 +57,23 @@ errors2 errors3 errors4 errors5 -errors6 \ No newline at end of file +errors6 +generics1 +generics2 +traits1 +traits2 +traits3 +traits4 +traits5 +quiz3 +lifetimes1 +lifetimes2 +lifetimes3 +tests1 +tests2 +tests3 +iterators1 +iterators2 +iterators3 +iterators4 +iterators5 \ No newline at end of file diff --git a/exercises/15_traits/traits1.rs b/exercises/15_traits/traits1.rs index 85be17ea..13757c5a 100644 --- a/exercises/15_traits/traits1.rs +++ b/exercises/15_traits/traits1.rs @@ -6,6 +6,9 @@ trait AppendBar { impl AppendBar for String { // TODO: Implement `AppendBar` for the type `String`. + fn append_bar(self) -> Self { + self+"Bar" + } } fn main() { diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index d724dc28..d33ab416 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -4,6 +4,12 @@ trait AppendBar { // TODO: Implement the trait `AppendBar` for a vector of strings. // `append_bar` should push the string "Bar" into the vector. +impl AppendBar for Vec{ + fn append_bar(mut self) -> Self { + self.push("Bar".to_string()); + self + } +} fn main() { // You can optionally experiment here. diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index c244650d..3bc15854 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -3,7 +3,9 @@ trait Licensed { // 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; + fn licensing_info(&self) -> String{ + "Default license".to_string() + } } struct SomeSoftware { diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index 80092a64..10583714 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -11,7 +11,7 @@ impl Licensed for SomeSoftware {} impl Licensed for OtherSoftware {} // TODO: Fix the compiler error by only changing the signature of this function. -fn compare_license_types(software1: ???, software2: ???) -> bool { +fn compare_license_types(software1: T, software2: U) -> bool { software1.licensing_info() == software2.licensing_info() } diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index 5b356ac7..f2fbdb12 100644 --- a/exercises/15_traits/traits5.rs +++ b/exercises/15_traits/traits5.rs @@ -19,7 +19,7 @@ impl SomeTrait for OtherStruct {} impl OtherTrait for OtherStruct {} // TODO: Fix the compiler error by only changing the signature of this function. -fn some_func(item: ???) -> bool { +fn some_func(item: T) -> bool { item.some_function() && item.other_function() } diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs index 19e2d398..ffa248bb 100644 --- a/exercises/16_lifetimes/lifetimes1.rs +++ b/exercises/16_lifetimes/lifetimes1.rs @@ -4,11 +4,11 @@ // not own their own data. What if their owner goes out of scope? // TODO: Fix the compiler error by updating the function signature. -fn longest(x: &str, y: &str) -> &str { +fn longest(x: &str, y: &str) -> String { if x.len() > y.len() { - x + x.to_string() } else { - y + y.to_string() } } diff --git a/exercises/16_lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs index de5a5dfc..409159ed 100644 --- a/exercises/16_lifetimes/lifetimes2.rs +++ b/exercises/16_lifetimes/lifetimes2.rs @@ -11,9 +11,10 @@ fn main() { // TODO: Fix the compiler error by moving one line. let string1 = String::from("long string is long"); + let string2 = String::from("xyz"); + let result; { - let string2 = String::from("xyz"); result = longest(&string1, &string2); } println!("The longest string is '{result}'"); diff --git a/exercises/16_lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs index 1cc27592..ad7c33d9 100644 --- a/exercises/16_lifetimes/lifetimes3.rs +++ b/exercises/16_lifetimes/lifetimes3.rs @@ -1,9 +1,9 @@ // Lifetimes are also needed when structs hold references. // TODO: Fix the compiler errors about the struct. -struct Book { - author: &str, - title: &str, +struct Book<'a> { + author: &'a str, + title: &'a str, } fn main() { diff --git a/exercises/17_tests/tests1.rs b/exercises/17_tests/tests1.rs index 7529f9f0..db9d726a 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -13,11 +13,11 @@ fn main() { mod tests { // TODO: Import `is_even`. You can use a wildcard to import everything in // the outer module. - + use super::*; #[test] fn you_can_assert() { // TODO: Test the function `is_even` with some values. - assert!(); - assert!(); - } + assert!(is_even(2)); + assert!(!is_even(3)); + } } diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index 0c6573e8..19bdf192 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -15,9 +15,9 @@ mod tests { #[test] fn you_can_assert_eq() { // TODO: Test the function `power_of_2` with some values. - assert_eq!(); - assert_eq!(); - assert_eq!(); - assert_eq!(); + assert_eq!(power_of_2(2),4); + assert_eq!(power_of_2(3),8); + assert_eq!(power_of_2(4),16); + assert_eq!(power_of_2(5),32); } } diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index 822184ef..ec117438 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -29,20 +29,22 @@ mod tests { // 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 + assert_eq!(rect.width, 10); // Check width + assert_eq!(rect.height, 20); // Check height } // TODO: This test should check if the program panics when we try to create // a rectangle with negative width. #[test] + #[should_panic] 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] + #[test] + #[should_panic] fn negative_height() { let _rect = Rectangle::new(10, -10); } diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index ca937ed0..2218bc89 100644 --- a/exercises/18_iterators/iterators1.rs +++ b/exercises/18_iterators/iterators1.rs @@ -13,13 +13,13 @@ mod tests { let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; // TODO: Create an iterator over the array. - let mut fav_fruits_iterator = todo!(); + let mut fav_fruits_iterator =my_fav_fruits.iter(); 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(&"custard apple")); // 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(&"peach")); // TODO: Replace `todo!()` assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); - assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` + assert_eq!(fav_fruits_iterator.next(), None); // TODO: Replace `todo!()` } } diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index 5903e657..c9c4a36a 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -5,24 +5,26 @@ // "hello" -> "Hello" fn capitalize_first(input: &str) -> String { let mut chars = input.chars(); - match chars.next() { + let char =match chars.next() { None => String::new(), - Some(first) => todo!(), - } + Some(first) => first.to_uppercase().collect(), + }; + + char+chars.as_str() } // TODO: Apply the `capitalize_first` function to a slice of string slices. // Return a vector of strings. // ["hello", "world"] -> ["Hello", "World"] fn capitalize_words_vector(words: &[&str]) -> Vec { - // ??? + words.iter().map(|word| capitalize_first(word)).collect() } // TODO: Apply the `capitalize_first` function again to a slice of string // slices. Return a single string. // ["hello", " ", "world"] -> "Hello World" fn capitalize_words_string(words: &[&str]) -> String { - // ??? + words.iter().map(|word| capitalize_first(word)).collect() } fn main() { diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 6b1eca17..42856909 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -1,3 +1,4 @@ + #[derive(Debug, PartialEq, Eq)] enum DivisionError { // Example: 42 / 0 @@ -11,21 +12,30 @@ enum DivisionError { // TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`. // Otherwise, return a suitable error. fn divide(a: i64, b: i64) -> Result { - todo!(); + if b==0 { return Err(DivisionError::DivideByZero) } ; + if a == i64::MIN && b == -1 { + Err(DivisionError::IntegerOverflow) + } else if a % b != 0 { + Err(DivisionError::NotDivisible) + } else { + Ok(a / b) + } } // TODO: Add the correct return type and complete the function body. // Desired output: `Ok([1, 11, 1426, 3])` -fn result_with_list() { +fn result_with_list() -> Result,DivisionError>{ let numbers = [27, 297, 38502, 81]; - let division_results = numbers.into_iter().map(|n| divide(n, 27)); + numbers.into_iter().map(|n| divide(n, 27)).collect() + } // 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() { +fn list_of_results()->Vec> { let numbers = [27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); + division_results.collect() } fn main() { diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index c296f0e4..42bc60ec 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -10,6 +10,7 @@ fn factorial(num: u64) -> u64 { // - additional variables // For an extra challenge, don't use: // - recursion + (1..=num).product() } fn main() { diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index 7e434cc5..b2f64cd6 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -28,6 +28,7 @@ fn count_for(map: &HashMap, value: Progress) -> usize { fn count_iterator(map: &HashMap, value: Progress) -> usize { // `map` is a hash map with `String` keys and `Progress` values. // map = { "variables1": Complete, "from_str": None, … } + map.values().filter(|&v| *v == value).count() } fn count_collection_for(collection: &[HashMap], value: Progress) -> usize { @@ -48,6 +49,11 @@ fn count_collection_iterator(collection: &[HashMap], value: Pr // `collection` is a slice of hash maps. // collection = [{ "variables1": Complete, "from_str": None, … }, // { "variables2": Complete, … }, … ] + collection + .iter() + .flat_map(|map| map.values()) + .filter(|&v| *v == value) + .count() } fn main() { diff --git a/exercises/quizzes/quiz3.rs b/exercises/quizzes/quiz3.rs index c877c5f8..5bb859fb 100644 --- a/exercises/quizzes/quiz3.rs +++ b/exercises/quizzes/quiz3.rs @@ -12,14 +12,14 @@ // block to support alphabetical report cards in addition to numerical ones. // TODO: Adjust the struct as described above. -struct ReportCard { - grade: f32, +struct ReportCard { + grade: T, student_name: String, student_age: u8, } // TODO: Adjust the impl block as described above. -impl ReportCard { +impl ReportCard { fn print(&self) -> String { format!( "{} ({}) - achieved a grade of {}", diff --git a/solutions/14_generics/generics2.rs b/solutions/14_generics/generics2.rs index dcf2377a..14f3f7af 100644 --- a/solutions/14_generics/generics2.rs +++ b/solutions/14_generics/generics2.rs @@ -1,4 +1,28 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +struct Wrapper { + value: T, +} + +impl Wrapper { + fn new(value: T) -> Self { + Wrapper { value } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn store_u32_in_wrapper() { + assert_eq!(Wrapper::new(42).value, 42); + } + + #[test] + fn store_str_in_wrapper() { + assert_eq!(Wrapper::new("Foo").value, "Foo"); + } } diff --git a/solutions/15_traits/traits1.rs b/solutions/15_traits/traits1.rs index dcf2377a..790873f4 100644 --- a/solutions/15_traits/traits1.rs +++ b/solutions/15_traits/traits1.rs @@ -1,4 +1,32 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// 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 { + fn append_bar(self) -> Self { + self + "Bar" + } +} + +fn main() { + let s = String::from("Foo"); + let s = s.append_bar(); + println!("s: {s}"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_foo_bar() { + assert_eq!(String::from("Foo").append_bar(), "FooBar"); + } + + #[test] + fn is_bar_bar() { + assert_eq!(String::from("").append_bar().append_bar(), "BarBar"); + } } diff --git a/solutions/15_traits/traits2.rs b/solutions/15_traits/traits2.rs index dcf2377a..0db93e0f 100644 --- a/solutions/15_traits/traits2.rs +++ b/solutions/15_traits/traits2.rs @@ -1,4 +1,27 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +trait AppendBar { + fn append_bar(self) -> Self; +} + +impl AppendBar for Vec { + fn append_bar(mut self) -> Self { + // ^^^ this is important + self.push(String::from("Bar")); + self + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_vec_pop_eq_bar() { + let mut foo = vec![String::from("Foo")].append_bar(); + assert_eq!(foo.pop().unwrap(), "Bar"); + assert_eq!(foo.pop().unwrap(), "Foo"); + } } diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs index dcf2377a..3d8ec85e 100644 --- a/solutions/15_traits/traits3.rs +++ b/solutions/15_traits/traits3.rs @@ -1,4 +1,36 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +trait Licensed { + fn licensing_info(&self) -> String { + "Default license".to_string() + } +} + +struct SomeSoftware { + version_number: i32, +} + +struct OtherSoftware { + version_number: String, +} + +impl Licensed for SomeSoftware {} +impl Licensed for OtherSoftware {} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_licensing_info_the_same() { + let licensing_info = "Default license"; + let some_software = SomeSoftware { version_number: 1 }; + let other_software = OtherSoftware { + version_number: "v2.0.0".to_string(), + }; + assert_eq!(some_software.licensing_info(), licensing_info); + assert_eq!(other_software.licensing_info(), licensing_info); + } } diff --git a/solutions/15_traits/traits4.rs b/solutions/15_traits/traits4.rs index dcf2377a..3675b8df 100644 --- a/solutions/15_traits/traits4.rs +++ b/solutions/15_traits/traits4.rs @@ -1,4 +1,35 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +trait Licensed { + fn licensing_info(&self) -> String { + "Default license".to_string() + } +} + +struct SomeSoftware; +struct OtherSoftware; + +impl Licensed for SomeSoftware {} +impl Licensed for OtherSoftware {} + +fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool { + // ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ + software1.licensing_info() == software2.licensing_info() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compare_license_information() { + assert!(compare_license_types(SomeSoftware, OtherSoftware)); + } + + #[test] + fn compare_license_information_backwards() { + assert!(compare_license_types(OtherSoftware, SomeSoftware)); + } } diff --git a/solutions/15_traits/traits5.rs b/solutions/15_traits/traits5.rs index dcf2377a..1fb426a4 100644 --- a/solutions/15_traits/traits5.rs +++ b/solutions/15_traits/traits5.rs @@ -1,4 +1,39 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +trait SomeTrait { + fn some_function(&self) -> bool { + true + } +} + +trait OtherTrait { + fn other_function(&self) -> bool { + true + } +} + +struct SomeStruct; +impl SomeTrait for SomeStruct {} +impl OtherTrait for SomeStruct {} + +struct OtherStruct; +impl SomeTrait for OtherStruct {} +impl OtherTrait for OtherStruct {} + +fn some_func(item: impl SomeTrait + OtherTrait) -> bool { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + item.some_function() && item.other_function() +} + +fn main() { + // 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/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs index dcf2377a..ca7b688d 100644 --- a/solutions/16_lifetimes/lifetimes1.rs +++ b/solutions/16_lifetimes/lifetimes1.rs @@ -1,4 +1,28 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// 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? + +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + // ^^^^ ^^ ^^ ^^ + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + // You can optionally experiment here. +} + +#[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/solutions/16_lifetimes/lifetimes2.rs b/solutions/16_lifetimes/lifetimes2.rs index dcf2377a..b0f2ef1f 100644 --- a/solutions/16_lifetimes/lifetimes2.rs +++ b/solutions/16_lifetimes/lifetimes2.rs @@ -1,4 +1,33 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + let string1 = String::from("long string is long"); + // Solution1: You can move `strings2` out of the inner block so that it is + // not dropped before the print statement. + let string2 = String::from("xyz"); + let result; + { + result = longest(&string1, &string2); + } + println!("The longest string is '{result}'"); + // `string2` dropped at the end of the function. + + // ========================================================================= + + let string1 = String::from("long string is long"); + let result; + { + let string2 = String::from("xyz"); + result = longest(&string1, &string2); + // Solution2: You can move the print statement into the inner block so + // that it is executed before `string2` is dropped. + println!("The longest string is '{result}'"); + // `string2` dropped here (end of the inner scope). + } } diff --git a/solutions/16_lifetimes/lifetimes3.rs b/solutions/16_lifetimes/lifetimes3.rs index dcf2377a..16a5a684 100644 --- a/solutions/16_lifetimes/lifetimes3.rs +++ b/solutions/16_lifetimes/lifetimes3.rs @@ -1,4 +1,18 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// Lifetimes are also needed when structs hold references. + +struct Book<'a> { + // ^^^^ added a lifetime annotation + author: &'a str, + // ^^ + title: &'a str, + // ^^ +} + +fn main() { + let book = Book { + author: "George Orwell", + title: "1984", + }; + + println!("{} by {}", book.title, book.author); } diff --git a/solutions/17_tests/tests1.rs b/solutions/17_tests/tests1.rs index dcf2377a..c52b8b16 100644 --- a/solutions/17_tests/tests1.rs +++ b/solutions/17_tests/tests1.rs @@ -1,4 +1,24 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// Tests are important to ensure that your code does what you think it should +// do. + +fn is_even(n: i64) -> bool { + n % 2 == 0 +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + // When writing unit tests, it is common to import everything from the outer + // module (`super`) using a wildcard. + use super::*; + + #[test] + fn you_can_assert() { + assert!(is_even(0)); + assert!(!is_even(-1)); + // ^ You can assert `false` using the negation operator `!`. + } } diff --git a/solutions/17_tests/tests2.rs b/solutions/17_tests/tests2.rs index dcf2377a..39a0005e 100644 --- a/solutions/17_tests/tests2.rs +++ b/solutions/17_tests/tests2.rs @@ -1,4 +1,22 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// 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 +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn you_can_assert_eq() { + assert_eq!(power_of_2(0), 1); + assert_eq!(power_of_2(1), 2); + assert_eq!(power_of_2(2), 4); + assert_eq!(power_of_2(3), 8); + } } diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs index dcf2377a..487fdc66 100644 --- a/solutions/17_tests/tests3.rs +++ b/solutions/17_tests/tests3.rs @@ -1,4 +1,45 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +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 must be positive"); + } + + Rectangle { width, height } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn correct_width_and_height() { + let rect = Rectangle::new(10, 20); + assert_eq!(rect.width, 10); // Check width + assert_eq!(rect.height, 20); // Check height + } + + #[test] + #[should_panic] // Added this attribute to check that the test panics. + fn negative_width() { + let _rect = Rectangle::new(-10, 10); + } + + #[test] + #[should_panic] // Added this attribute to check that the test panics. + fn negative_height() { + let _rect = Rectangle::new(10, -10); + } } diff --git a/solutions/18_iterators/iterators1.rs b/solutions/18_iterators/iterators1.rs index dcf2377a..93a6008a 100644 --- a/solutions/18_iterators/iterators1.rs +++ b/solutions/18_iterators/iterators1.rs @@ -1,4 +1,26 @@ +// 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. + fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn iterators() { + let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; + + // Create an iterator over the array. + let mut fav_fruits_iterator = my_fav_fruits.iter(); + + assert_eq!(fav_fruits_iterator.next(), Some(&"banana")); + assert_eq!(fav_fruits_iterator.next(), Some(&"custard apple")); + assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); + assert_eq!(fav_fruits_iterator.next(), Some(&"peach")); + assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); + assert_eq!(fav_fruits_iterator.next(), None); + // ^^^^ reached the end + } } diff --git a/solutions/18_iterators/iterators2.rs b/solutions/18_iterators/iterators2.rs index dcf2377a..db05f293 100644 --- a/solutions/18_iterators/iterators2.rs +++ b/solutions/18_iterators/iterators2.rs @@ -1,4 +1,56 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// In this exercise, you'll learn some of the unique advantages that iterators +// can offer. + +// "hello" -> "Hello" +fn capitalize_first(input: &str) -> String { + let mut chars = input.chars(); + match chars.next() { + None => String::new(), + Some(first) => first.to_uppercase().to_string() + chars.as_str(), + } +} + +// Apply the `capitalize_first` function to a slice of string slices. +// Return a vector of strings. +// ["hello", "world"] -> ["Hello", "World"] +fn capitalize_words_vector(words: &[&str]) -> Vec { + words.iter().map(|word| capitalize_first(word)).collect() +} + +// Apply the `capitalize_first` function again to a slice of string +// slices. Return a single string. +// ["hello", " ", "world"] -> "Hello World" +fn capitalize_words_string(words: &[&str]) -> String { + words.iter().map(|word| capitalize_first(word)).collect() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_success() { + assert_eq!(capitalize_first("hello"), "Hello"); + } + + #[test] + fn test_empty() { + assert_eq!(capitalize_first(""), ""); + } + + #[test] + fn test_iterate_string_vec() { + let words = vec!["hello", "world"]; + assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]); + } + + #[test] + fn test_iterate_into_string() { + let words = vec!["hello", " ", "world"]; + assert_eq!(capitalize_words_string(&words), "Hello World"); + } } diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs index dcf2377a..11aa1ec8 100644 --- a/solutions/18_iterators/iterators3.rs +++ b/solutions/18_iterators/iterators3.rs @@ -1,4 +1,86 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +#[derive(Debug, PartialEq, Eq)] +enum DivisionError { + // Example: 42 / 0 + DivideByZero, + // Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1` + IntegerOverflow, + // Example: 5 / 2 = 2.5 + NotDivisible, +} + +fn divide(a: i64, b: i64) -> Result { + if b == 0 { + return Err(DivisionError::DivideByZero); + } + + if a == i64::MIN && b == -1 { + return Err(DivisionError::IntegerOverflow); + } + + if a % b != 0 { + return Err(DivisionError::NotDivisible); + } + + Ok(a / b) +} + +fn result_with_list() -> Result, DivisionError> { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let numbers = [27, 297, 38502, 81]; + let division_results = numbers.into_iter().map(|n| divide(n, 27)); + // Collects to the expected return type. Returns the first error in the + // division results (if one exists). + division_results.collect() +} + +fn list_of_results() -> Vec> { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let numbers = [27, 297, 38502, 81]; + let division_results = numbers.into_iter().map(|n| divide(n, 27)); + // Collects to the expected return type. + division_results.collect() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_success() { + assert_eq!(divide(81, 9), Ok(9)); + } + + #[test] + fn test_divide_by_0() { + assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); + } + + #[test] + fn test_integer_overflow() { + assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow)); + } + + #[test] + fn test_not_divisible() { + assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); + } + + #[test] + fn test_divide_0_by_something() { + assert_eq!(divide(0, 81), Ok(0)); + } + + #[test] + fn test_result_with_list() { + assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]); + } + + #[test] + fn test_list_of_results() { + assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]); + } } diff --git a/solutions/18_iterators/iterators4.rs b/solutions/18_iterators/iterators4.rs index dcf2377a..4168835a 100644 --- a/solutions/18_iterators/iterators4.rs +++ b/solutions/18_iterators/iterators4.rs @@ -1,4 +1,72 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// 3 possible solutions are presented. + +// With `for` loop and a mutable variable. +fn factorial_for(num: u64) -> u64 { + let mut result = 1; + + for x in 2..=num { + result *= x; + } + + result +} + +// Equivalent to `factorial_for` but shorter and without a `for` loop and +// mutable variables. +fn factorial_fold(num: u64) -> u64 { + // Case num==0: The iterator 2..=0 is empty + // -> The initial value of `fold` is returned which is 1. + // Case num==1: The iterator 2..=1 is also empty + // -> The initial value 1 is returned. + // Case num==2: The iterator 2..=2 contains one element + // -> The initial value 1 is multiplied by 2 and the result + // is returned. + // Case num==3: The iterator 2..=3 contains 2 elements + // -> 1 * 2 is calculated, then the result 2 is multiplied by + // the second element 3 so the result 6 is returned. + // And so on… + #[allow(clippy::unnecessary_fold)] + (2..=num).fold(1, |acc, x| acc * x) +} + +// Equivalent to `factorial_fold` but with a built-in method that is suggested +// by Clippy. +fn factorial_product(num: u64) -> u64 { + (2..=num).product() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn factorial_of_0() { + assert_eq!(factorial_for(0), 1); + assert_eq!(factorial_fold(0), 1); + assert_eq!(factorial_product(0), 1); + } + + #[test] + fn factorial_of_1() { + assert_eq!(factorial_for(1), 1); + assert_eq!(factorial_fold(1), 1); + assert_eq!(factorial_product(1), 1); + } + #[test] + fn factorial_of_2() { + assert_eq!(factorial_for(2), 2); + assert_eq!(factorial_fold(2), 2); + assert_eq!(factorial_product(2), 2); + } + + #[test] + fn factorial_of_4() { + assert_eq!(factorial_for(4), 24); + assert_eq!(factorial_fold(4), 24); + assert_eq!(factorial_product(4), 24); + } } diff --git a/solutions/18_iterators/iterators5.rs b/solutions/18_iterators/iterators5.rs index dcf2377a..067a117b 100644 --- a/solutions/18_iterators/iterators5.rs +++ b/solutions/18_iterators/iterators5.rs @@ -1,4 +1,168 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// 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 to not use imperative loops (for/while). + +use std::collections::HashMap; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Progress { + None, + Some, + Complete, +} + +fn count_for(map: &HashMap, value: Progress) -> usize { + let mut count = 0; + for val in map.values() { + if *val == value { + count += 1; + } + } + count +} + +fn count_iterator(map: &HashMap, value: Progress) -> usize { + // `map` is a hash map with `String` keys and `Progress` values. + // map = { "variables1": Complete, "from_str": None, … } + map.values().filter(|val| **val == value).count() +} + +fn count_collection_for(collection: &[HashMap], value: Progress) -> usize { + let mut count = 0; + for map in collection { + count += count_for(map, value); + } + count +} + +fn count_collection_iterator(collection: &[HashMap], value: Progress) -> usize { + // `collection` is a slice of hash maps. + // collection = [{ "variables1": Complete, "from_str": None, … }, + // { "variables2": Complete, … }, … ] + collection + .iter() + .map(|map| count_iterator(map, value)) + .sum() +} + +// Equivalent to `count_collection_iterator` and `count_iterator`, iterating as +// if the collection was a single container instead of a container of containers +// (and more accurately, a single iterator instead of an iterator of iterators). +fn count_collection_iterator_flat( + collection: &[HashMap], + value: Progress, +) -> usize { + // `collection` is a slice of hash maps. + // collection = [{ "variables1": Complete, "from_str": None, … }, + // { "variables2": Complete, … }, … ] + collection + .iter() + .flat_map(HashMap::values) // or just `.flatten()` when wanting the default iterator (`HashMap::iter`) + .filter(|val| **val == value) + .count() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + use Progress::*; + + fn get_map() -> HashMap { + 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> { + 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!(count_iterator(&map, Complete), 3); + } + + #[test] + fn count_some() { + let map = get_map(); + assert_eq!(count_iterator(&map, Some), 1); + } + + #[test] + fn count_none() { + let map = get_map(); + assert_eq!(count_iterator(&map, None), 2); + } + + #[test] + fn count_complete_equals_for() { + let map = get_map(); + let progress_states = [Complete, Some, None]; + for progress_state in progress_states { + assert_eq!( + count_for(&map, progress_state), + count_iterator(&map, progress_state), + ); + } + } + + #[test] + fn count_collection_complete() { + let collection = get_vec_map(); + assert_eq!(count_collection_iterator(&collection, Complete), 6); + assert_eq!(count_collection_iterator_flat(&collection, Complete), 6); + } + + #[test] + fn count_collection_some() { + let collection = get_vec_map(); + assert_eq!(count_collection_iterator(&collection, Some), 1); + assert_eq!(count_collection_iterator_flat(&collection, Some), 1); + } + + #[test] + fn count_collection_none() { + let collection = get_vec_map(); + assert_eq!(count_collection_iterator(&collection, None), 4); + assert_eq!(count_collection_iterator_flat(&collection, None), 4); + } + + #[test] + fn count_collection_equals_for() { + let collection = get_vec_map(); + let progress_states = [Complete, Some, None]; + + for progress_state in progress_states { + assert_eq!( + count_collection_for(&collection, progress_state), + count_collection_iterator(&collection, progress_state), + ); + assert_eq!( + count_collection_for(&collection, progress_state), + count_collection_iterator_flat(&collection, progress_state), + ); + } + } } diff --git a/solutions/quizzes/quiz3.rs b/solutions/quizzes/quiz3.rs index dcf2377a..7b912782 100644 --- a/solutions/quizzes/quiz3.rs +++ b/solutions/quizzes/quiz3.rs @@ -1,4 +1,65 @@ -fn main() { - // DON'T EDIT THIS SOLUTION FILE! - // It will be automatically filled after you finish the exercise. +// An imaginary magical school has a new report card generation system written +// 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 in addition to numerical ones. + +use std::fmt::Display; + +// Make the struct generic over `T`. +struct ReportCard { + // ^^^ + grade: T, + // ^ + student_name: String, + student_age: u8, +} + +// To be able to print the grade, it has to implement the `Display` trait. +impl ReportCard { + // ^^^^^^^ require that `T` implements `Display`. + 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::*; + + #[test] + fn generate_numeric_report_card() { + let report_card = ReportCard { + grade: 2.1, + student_name: "Tom Wriggle".to_string(), + student_age: 12, + }; + assert_eq!( + report_card.print(), + "Tom Wriggle (12) - achieved a grade of 2.1", + ); + } + + #[test] + fn generate_alphabetic_report_card() { + let report_card = ReportCard { + grade: "A+", + student_name: "Gary Plotter".to_string(), + student_age: 11, + }; + assert_eq!( + report_card.print(), + "Gary Plotter (11) - achieved a grade of A+", + ); + } }