From 9d46b94f9fb0653decf3cf22a9bdffbb2ae3896f Mon Sep 17 00:00:00 2001 From: Yuhua Mai <yuhua@urbandoor.com> Date: Sat, 16 May 2020 11:52:56 -0700 Subject: [PATCH] Finish exercises --- exercises/conversions/as_ref_mut.rs | 5 ++- exercises/conversions/from_into.rs | 17 ++++++++- exercises/conversions/from_str.rs | 15 ++++++-- exercises/conversions/try_from_into.rs | 17 +++++++-- exercises/conversions/using_as.rs | 9 ++--- exercises/generics/generics1.rs | 3 +- exercises/generics/generics2.rs | 17 ++++----- exercises/generics/generics3.rs | 35 ++++++++++++------- exercises/standard_library_types/arc1.rs | 7 ++-- .../standard_library_types/iterators2.rs | 11 +++--- .../standard_library_types/iterators3.rs | 24 +++++++++---- .../standard_library_types/iterators4.rs | 6 +++- exercises/threads/threads1.rs | 19 +++++----- exercises/traits/traits1.rs | 7 ++-- exercises/traits/traits2.rs | 18 +++++----- 15 files changed, 134 insertions(+), 76 deletions(-) diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs index aa1e623..c9d50c5 100644 --- a/exercises/conversions/as_ref_mut.rs +++ b/exercises/conversions/as_ref_mut.rs @@ -2,16 +2,15 @@ // 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. -// I AM NOT DONE // Obtain the number of bytes (not characters) in the given argument // Add the AsRef trait appropriately as a trait bound -fn byte_counter<T>(arg: T) -> usize { +fn byte_counter<T: AsRef<str>>(arg: T) -> usize { arg.as_ref().as_bytes().len() } // Obtain the number of characters (not bytes) in the given argument // Add the AsRef trait appropriately as a trait bound -fn char_counter<T>(arg: T) -> usize { +fn char_counter<T: AsRef<str> >(arg: T) -> usize { arg.as_ref().chars().count() } diff --git a/exercises/conversions/from_into.rs b/exercises/conversions/from_into.rs index 58b860e..19053ff 100644 --- a/exercises/conversions/from_into.rs +++ b/exercises/conversions/from_into.rs @@ -18,7 +18,6 @@ impl Default for Person { } } -// I AM NOT DONE // 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` @@ -34,6 +33,18 @@ impl Default for Person { // Otherwise, then return an instantiated Person onject with the results impl From<&str> for Person { fn from(s: &str) -> Person { + if s.is_empty() { + return Default::default(); + } + + let splits: Vec<&str> = s.split(",").collect::<Vec<&str>>(); + let name = splits[0].to_string(); + let age_result = splits[1].parse::<usize>(); + + match age_result { + Ok(age) => Person { name, age }, + Error => Default::default(), + } } } @@ -49,6 +60,7 @@ fn main() { #[cfg(test)] mod tests { use super::*; + #[test] fn test_default() { // Test that the default person is 30 year old John @@ -56,6 +68,7 @@ mod tests { 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 @@ -63,6 +76,7 @@ mod tests { assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } + #[test] fn test_good_convert() { // Test that "Mark,20" works @@ -70,6 +84,7 @@ mod tests { 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 diff --git a/exercises/conversions/from_str.rs b/exercises/conversions/from_str.rs index 3c889d7..57beff9 100644 --- a/exercises/conversions/from_str.rs +++ b/exercises/conversions/from_str.rs @@ -10,7 +10,6 @@ struct Person { age: usize, } -// I AM NOT DONE // Steps: // 1. If the length of the provided string is 0, then return an error // 2. Split the given string on the commas present in it @@ -21,6 +20,18 @@ struct Person { impl FromStr for Person { type Err = String; fn from_str(s: &str) -> Result<Person, Self::Err> { + if s.is_empty() { + return Err("empty &str".to_string()); + } + + let splits: Vec<&str> = s.split(",").collect::<Vec<&str>>(); + let name = splits[0].to_string(); + let age_result = splits[1].parse::<usize>(); + + match age_result { + Ok(age) => Ok(Person { name, age }), + Error => Err("incorrect age".to_string()), + } } } @@ -46,4 +57,4 @@ mod tests { fn missing_age() { "John".parse::<Person>().unwrap(); } -} \ No newline at end of file +} diff --git a/exercises/conversions/try_from_into.rs b/exercises/conversions/try_from_into.rs index 255d2e1..82fda30 100644 --- a/exercises/conversions/try_from_into.rs +++ b/exercises/conversions/try_from_into.rs @@ -2,7 +2,7 @@ // 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 https://doc.rust-lang.org/std/convert/trait.TryFrom.html -use std::convert::{TryInto, TryFrom}; +use std::convert::{TryFrom, TryInto}; #[derive(Debug)] struct Person { @@ -10,7 +10,6 @@ struct Person { age: usize, } -// I AM NOT DONE // Your task is to complete this implementation // in order for the line `let p = Person::try_from("Mark,20")` to compile // and return an Ok result of inner type Person. @@ -28,6 +27,18 @@ struct Person { impl TryFrom<&str> for Person { type Error = String; fn try_from(s: &str) -> Result<Self, Self::Error> { + if s.is_empty() { + return Err("empty &str".to_string()); + } + + let splits: Vec<&str> = s.split(",").collect::<Vec<&str>>(); + let name = splits[0].to_string(); + let age_result = splits[1].parse::<usize>(); + + match age_result { + Ok(age) => Ok(Person { name, age }), + Error => Err("incorrect age".to_string()), + } } } @@ -68,4 +79,4 @@ mod tests { fn test_panic_bad_age() { let p = Person::try_from("Mark,twenty").unwrap(); } -} \ No newline at end of file +} diff --git a/exercises/conversions/using_as.rs b/exercises/conversions/using_as.rs index 54f9651..8e8264f 100644 --- a/exercises/conversions/using_as.rs +++ b/exercises/conversions/using_as.rs @@ -2,16 +2,13 @@ // Please note that the `as` operator is not only used when type casting. // It also helps with renaming imports. -// I AM NOT DONE // The goal is to make sure that the division does not fail to compile fn average(values: &[f64]) -> f64 { - let total = values - .iter() - .fold(0.0, |a, b| a + b); - total / values.len() + let total = values.iter().fold(0.0, |a, b| a + *b); + total / values.len() as f64 } fn main() { let values = [3.5, 0.3, 13.0, 11.7]; println!("{}", average(&values)); -} \ No newline at end of file +} diff --git a/exercises/generics/generics1.rs b/exercises/generics/generics1.rs index d075a4d..a441ed2 100644 --- a/exercises/generics/generics1.rs +++ b/exercises/generics/generics1.rs @@ -1,10 +1,9 @@ // This shopping list program isn't compiling! // Use your knowledge of generics to fix it. -// I AM NOT DONE fn main() { - let mut shopping_list: Vec<?> = Vec::new(); + let mut shopping_list: Vec<&str> = Vec::new(); shopping_list.push("milk"); } diff --git a/exercises/generics/generics2.rs b/exercises/generics/generics2.rs index 1fe5041..57ae948 100644 --- a/exercises/generics/generics2.rs +++ b/exercises/generics/generics2.rs @@ -1,13 +1,12 @@ // This powerful wrapper provides the ability to store a positive integer value. // Rewrite it using generics so that it supports wrapping ANY type. -// I AM NOT DONE -struct Wrapper<u32> { - value: u32 +struct Wrapper<T> { + value: T, } -impl<u32> Wrapper<u32> { - pub fn new(value: u32) -> Self { +impl<T> Wrapper<T> { + pub fn new(value: T) -> Self { Wrapper { value } } } @@ -18,13 +17,11 @@ mod tests { #[test] fn store_u32_in_wrapper() { - assert_eq!(Wrapper::new(42).value, 42); + assert_eq!(Wrapper::new(42).value, 42); } #[test] fn store_str_in_wrapper() { - // TODO: Delete this assert and uncomment the one below once you have finished the exercise. - assert!(false); - // assert_eq!(Wrapper::new("Foo").value, "Foo"); + assert_eq!(Wrapper::new("Foo").value, "Foo"); } -} \ No newline at end of file +} diff --git a/exercises/generics/generics3.rs b/exercises/generics/generics3.rs index cd3232a..8cd6b97 100644 --- a/exercises/generics/generics3.rs +++ b/exercises/generics/generics3.rs @@ -1,21 +1,24 @@ // 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 +// 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 to support alphabetical report cards, thereby making the second // test pass. +use std::fmt::Display; -// I AM NOT DONE -pub struct ReportCard { - pub grade: f32, +pub struct ReportCard<T> { + pub grade: T, pub student_name: String, pub student_age: u8, } -impl ReportCard { +impl<T: Display> ReportCard<T> { pub fn print(&self) -> String { - format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, &self.grade) + format!( + "{} ({}) - achieved a grade of {}", + &self.student_name, &self.student_age, &self.grade + ) } } @@ -26,21 +29,27 @@ mod tests { #[test] fn generate_numeric_report_card() { let report_card = ReportCard { - grade: 2.1, - student_name: "Tom Wriggle".to_string(), + 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"); + assert_eq!( + report_card.print(), + "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, - student_name: "Gary Plotter".to_string(), + grade: "A+".to_string(), + student_name: "Gary Plotter".to_string(), student_age: 11, }; - assert_eq!(report_card.print(), "Gary Plotter (11) - achieved a grade of A+"); + assert_eq!( + report_card.print(), + "Gary Plotter (11) - achieved a grade of A+" + ); } -} \ No newline at end of file +} diff --git a/exercises/standard_library_types/arc1.rs b/exercises/standard_library_types/arc1.rs index 9784e84..a3a527f 100644 --- a/exercises/standard_library_types/arc1.rs +++ b/exercises/standard_library_types/arc1.rs @@ -4,20 +4,21 @@ // somewhere. Try not to create any copies of the `numbers` Vec! // Execute `rustlings hint arc1` for hints :) -// I AM NOT DONE - use std::sync::Arc; use std::thread; fn main() { let numbers: Vec<_> = (0..100u32).collect(); - let shared_numbers = // TODO + let shared_numbers = Arc::new(numbers); let mut joinhandles = Vec::new(); for offset in 0..8 { + let child_numbers = shared_numbers.clone(); + joinhandles.push(thread::spawn(move || { let mut i = offset; let mut sum = 0; + while i < child_numbers.len() { sum += child_numbers[i]; i += 5; diff --git a/exercises/standard_library_types/iterators2.rs b/exercises/standard_library_types/iterators2.rs index a1274a2..2c50371 100644 --- a/exercises/standard_library_types/iterators2.rs +++ b/exercises/standard_library_types/iterators2.rs @@ -5,13 +5,14 @@ // Step 3. Apply the `capitalize_first` function again to a list, but try and ensure it returns a single string // As always, there are hints if you execute `rustlings hint iterators2`! -// I AM NOT DONE + +use std::str::Chars; pub fn capitalize_first(input: &str) -> String { - let mut c = input.chars(); + let mut c: Chars = input.chars(); match c.next() { None => String::new(), - Some(first) => first.collect::<String>() + c.as_str(), + Some(first) => first.to_uppercase().collect::<String>() + c.as_str(), } } @@ -35,14 +36,14 @@ mod tests { #[test] fn test_iterate_string_vec() { let words = vec!["hello", "world"]; - let capitalized_words: Vec<String> = // TODO + let capitalized_words: Vec<String> = words.iter().map(|&c| capitalize_first(c)).collect(); assert_eq!(capitalized_words, ["Hello", "World"]); } #[test] fn test_iterate_into_string() { let words = vec!["hello", " ", "world"]; - let capitalized_words = // TODO + let capitalized_words = words.iter().map(|&c| capitalize_first(c)).collect::<String>(); assert_eq!(capitalized_words, "Hello World"); } } diff --git a/exercises/standard_library_types/iterators3.rs b/exercises/standard_library_types/iterators3.rs index 353cea6..e798cde 100644 --- a/exercises/standard_library_types/iterators3.rs +++ b/exercises/standard_library_types/iterators3.rs @@ -7,7 +7,6 @@ // Execute `rustlings hint iterators3` to get some hints! // Have fun :-) -// I AM NOT DONE #[derive(Debug, PartialEq, Eq)] pub enum DivisionError { @@ -24,7 +23,18 @@ pub struct NotDivisibleError { // This function should calculate `a` divided by `b` if `a` is // evenly divisible by b. // Otherwise, it should return a suitable error. -pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {} +pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> { + if b == 0 { + Err(DivisionError::DivideByZero) + } else if a % b != 0 { + Err(DivisionError::NotDivisible(NotDivisibleError { + dividend: a, + divisor: b, + })) + } else { + Ok(a / b) + } +} #[cfg(test)] mod tests { @@ -42,7 +52,7 @@ mod tests { divide(81, 6), Err(DivisionError::NotDivisible(NotDivisibleError { dividend: 81, - divisor: 6 + divisor: 6, })) ); } @@ -58,12 +68,12 @@ mod tests { } // Iterator exercises using your `divide` function - /* + #[test] fn result_with_list() { let numbers = vec![27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); - let x //... Fill in here! + let x = division_results.collect::<Result<Vec<i32>, _>>(); assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])"); } @@ -71,8 +81,8 @@ mod tests { fn list_of_results() { let numbers = vec![27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); - let x //... Fill in here! + let x = division_results.collect::<Vec<Result<i32, DivisionError>>>(); assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]"); } - */ + } diff --git a/exercises/standard_library_types/iterators4.rs b/exercises/standard_library_types/iterators4.rs index b945613..f38d374 100644 --- a/exercises/standard_library_types/iterators4.rs +++ b/exercises/standard_library_types/iterators4.rs @@ -1,6 +1,5 @@ // iterators4.rs -// I AM NOT DONE pub fn factorial(num: u64) -> u64 { // Complete this function to return factorial of num @@ -12,6 +11,11 @@ pub fn factorial(num: u64) -> u64 { // For the most fun don't use: // - recursion // Execute `rustlings hint iterators4` for hints. + + match num { + 1 => 1, + n => n * factorial(n - 1) + } } #[cfg(test)] diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs index 1785e8c..c6c7e85 100644 --- a/exercises/threads/threads1.rs +++ b/exercises/threads/threads1.rs @@ -5,9 +5,10 @@ // of "waiting..." and the program ends without timing out when running, // you've got it :) -// I AM NOT DONE use std::sync::Arc; +use std::sync::Mutex; +use std::sync::MutexGuard; use std::thread; use std::time::Duration; @@ -16,16 +17,18 @@ struct JobStatus { } fn main() { - let status = Arc::new(JobStatus { jobs_completed: 0 }); + let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); let status_shared = status.clone(); thread::spawn(move || { - for _ in 0..10 { - thread::sleep(Duration::from_millis(250)); - status_shared.jobs_completed += 1; + for _ in 0..100 { + thread::sleep(Duration::from_millis(25)); + let mut sharedJobStatus: MutexGuard<JobStatus> = status_shared.lock().unwrap(); + let curr_jobs_completed: u32 = sharedJobStatus.jobs_completed as u32; + *sharedJobStatus = JobStatus { jobs_completed: curr_jobs_completed + 1 }; } }); - while status.jobs_completed < 10 { - println!("waiting... "); - thread::sleep(Duration::from_millis(500)); + while status.lock().unwrap().jobs_completed < 20 { + println!("waiting... {}", status.lock().unwrap().jobs_completed); + thread::sleep(Duration::from_millis(25)); } } diff --git a/exercises/traits/traits1.rs b/exercises/traits/traits1.rs index 8253ef8..09c04d4 100644 --- a/exercises/traits/traits1.rs +++ b/exercises/traits/traits1.rs @@ -8,14 +8,15 @@ // which appends "Bar" to any object // implementing this trait. -// I AM NOT DONE trait AppendBar { fn append_bar(self) -> Self; } impl AppendBar for String { - //Add your code here + fn append_bar(self) -> Self { + self + "Bar" + } } fn main() { @@ -41,4 +42,4 @@ mod tests { ); } -} \ No newline at end of file +} diff --git a/exercises/traits/traits2.rs b/exercises/traits/traits2.rs index 7f5014d..dbadf8e 100644 --- a/exercises/traits/traits2.rs +++ b/exercises/traits/traits2.rs @@ -1,25 +1,26 @@ // 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! -// I AM NOT DONE trait AppendBar { fn append_bar(self) -> Self; } -//TODO: Add your code here - - - +impl AppendBar for Vec<String> { + fn append_bar(mut self) -> Self { + self.push("Bar".to_string()); + self + } +} #[cfg(test)] mod tests { @@ -31,5 +32,4 @@ mod tests { assert_eq!(foo.pop().unwrap(), String::from("Bar")); assert_eq!(foo.pop().unwrap(), String::from("Foo")); } - }