diff --git a/.rustlings-state.txt b/.rustlings-state.txt index 888d577..ace931e 100644 --- a/.rustlings-state.txt +++ b/.rustlings-state.txt @@ -1,6 +1,6 @@ DON'T EDIT THIS FILE! -lifetimes1 +iterators2 intro1 intro2 @@ -65,4 +65,11 @@ traits2 traits3 traits4 traits5 -quiz3 \ No newline at end of file +quiz3 +lifetimes1 +lifetimes2 +lifetimes3 +tests1 +tests2 +tests3 +iterators1 \ No newline at end of file diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs index 19e2d39..683bd75 100644 --- a/exercises/16_lifetimes/lifetimes1.rs +++ b/exercises/16_lifetimes/lifetimes1.rs @@ -4,7 +4,7 @@ // 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<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { diff --git a/exercises/16_lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs index de5a5df..13bc897 100644 --- a/exercises/16_lifetimes/lifetimes2.rs +++ b/exercises/16_lifetimes/lifetimes2.rs @@ -15,6 +15,7 @@ fn main() { { let string2 = String::from("xyz"); 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 1cc2759..31436e5 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 7529f9f..191f4e5 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -14,10 +14,12 @@ mod tests { // TODO: Import `is_even`. You can use a wildcard to import everything in // the outer module. + use crate::is_even; + #[test] fn you_can_assert() { // TODO: Test the function `is_even` with some values. - assert!(); - assert!(); + assert!(is_even(4)); + assert!(is_even(6)); } } diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index 0c6573e..2037be3 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(2), 4); + assert_eq!(power_of_2(2), 4); + assert_eq!(power_of_2(2), 4); } } diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index 822184e..300a742 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -26,23 +26,20 @@ mod tests { #[test] 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 + 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] + #[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 ca937ed..c36252a 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")); 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")); 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); } } diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index 5903e65..7242d83 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -1,28 +1,35 @@ // In this exercise, you'll learn some of the unique advantages that iterators // can offer. -// TODO: Complete the `capitalize_first` function. +// Complete the `capitalize_first` function. // "hello" -> "Hello" fn capitalize_first(input: &str) -> String { let mut chars = input.chars(); match chars.next() { None => String::new(), - Some(first) => todo!(), + Some(first) => { + let mut new = String::from(first.to_uppercase().to_string()); + new.push_str(&input[1..]); + + new + }, } } -// 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(|i| { + capitalize_first(i) + }).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(|i| { + capitalize_first(i) + }).collect::>().join("") } fn main() { diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 6b1eca1..1ce679f 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -1,3 +1,5 @@ +use std::f32::consts::E; + #[derive(Debug, PartialEq, Eq)] enum DivisionError { // Example: 42 / 0 @@ -8,24 +10,43 @@ enum DivisionError { NotDivisible, } -// 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) + } + match a.checked_rem(b) { + None => { + Err(DivisionError::IntegerOverflow) + } + Some(0) => { + let division = a.checked_div(b); + if let Some(result) = division { + Ok(result) + } else { + Err(DivisionError::IntegerOverflow) + } + }, + Some(_) => { + Err(DivisionError::NotDivisible) + } + } } -// 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)); + Ok(division_results.map(|i| { + i.unwrap() + }).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 c296f0e..585347a 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -10,6 +10,9 @@ fn factorial(num: u64) -> u64 { // - additional variables // For an extra challenge, don't use: // - recursion + (1..=num).fold(1, |acc, v| { + acc * v + }) } fn main() { diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index 7e434cc..d375892 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -23,11 +23,14 @@ fn count_for(map: &HashMap, value: Progress) -> usize { count } -// TODO: Implement the functionality of `count_for` but with an iterator instead +// Implement the functionality of `count_for` but with an iterator instead // of a `for` loop. 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.iter().filter(|p| { + *p.1 == value + }).count() } fn count_collection_for(collection: &[HashMap], value: Progress) -> usize { @@ -42,12 +45,13 @@ fn count_collection_for(collection: &[HashMap], value: Progres count } -// TODO: Implement the functionality of `count_collection_for` but with an +// Implement the functionality of `count_collection_for` but with an // iterator instead of a `for` loop. 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(|i| {count_iterator(i, value)}).sum() } fn main() { diff --git a/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs index dcf2377..ca7b688 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 dcf2377..b0f2ef1 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 dcf2377..16a5a68 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 dcf2377..c52b8b1 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 dcf2377..39a0005 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 dcf2377..487fdc6 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 dcf2377..93a6008 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 dcf2377..db05f29 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"); + } }