diff --git a/exercises/23_conversions/1_using_as.rs b/exercises/23_conversions/1_using_as.rs index 414cef3..343bfe2 100644 --- a/exercises/23_conversions/1_using_as.rs +++ b/exercises/23_conversions/1_using_as.rs @@ -10,11 +10,9 @@ // Execute `rustlings hint using_as` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn average(values: &[f64]) -> f64 { let total = values.iter().sum::(); - total / values.len() + total / values.len() as f64 // Total is f64, so we need to cast values.len() (type usize) to f64 } fn main() { diff --git a/exercises/23_conversions/2_from_into.rs b/exercises/23_conversions/2_from_into.rs index 11787c3..58cd4d9 100644 --- a/exercises/23_conversions/2_from_into.rs +++ b/exercises/23_conversions/2_from_into.rs @@ -24,7 +24,6 @@ impl Default for Person { } } - // Your task is to complete this implementation in order for the line `let p1 = // 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::()`. The @@ -41,10 +40,34 @@ impl Default for Person { // 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 - impl From<&str> for Person { - fn from(s: &str) -> Person {} + fn from(s: &str) -> Person { + let parts: Vec<&str> = s.split(',').collect(); + + // Attempt to parse the name and age from the string + // https://doc.rust-lang.org/book/ch06-03-if-let.html + if let [name, age_str] = parts.as_slice() { + // If the name is empty, return the default person + if name.is_empty() { + return Person::default(); + } + // If the converted age is valid, return the person + if let Ok(age) = age_str.parse::() { + return Person { + name: name.to_string(), + age, + }; + } + } + // Otherwise, return the default person + return Person::default(); + } +} +/// Convert a Person struct back into a String +impl Into for Person { + fn into(self) -> String { + format!("{},{}", self.name, self.age) + } } fn main() { diff --git a/exercises/23_conversions/3_from_str.rs b/exercises/23_conversions/3_from_str.rs index e209347..1ad7c29 100644 --- a/exercises/23_conversions/3_from_str.rs +++ b/exercises/23_conversions/3_from_str.rs @@ -31,8 +31,6 @@ enum ParsePersonError { 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 @@ -48,6 +46,24 @@ enum ParsePersonError { impl FromStr for Person { type Err = ParsePersonError; fn from_str(s: &str) -> Result { + if s.is_empty() { + return Err(ParsePersonError::Empty); + } + let parts: Vec<&str> = s.split(',').collect(); + if parts.len() != 2 { + return Err(ParsePersonError::BadLen); + } + let name = parts[0]; + if name.is_empty() { + return Err(ParsePersonError::NoName); + } + let age = parts[1] + .parse::() + .map_err(ParsePersonError::ParseInt)?; + Ok(Person { + name: name.to_string(), + age, + }) } } diff --git a/exercises/23_conversions/4_try_from_into.rs b/exercises/23_conversions/4_try_from_into.rs index 32d6ef3..a1ee3d6 100644 --- a/exercises/23_conversions/4_try_from_into.rs +++ b/exercises/23_conversions/4_try_from_into.rs @@ -36,11 +36,25 @@ enum IntoColorError { // 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. +fn is_valid_rgb_value(value: i16) -> bool { + (0..=255).contains(&value) +} // Tuple implementation impl TryFrom<(i16, i16, i16)> for Color { type Error = IntoColorError; fn try_from(tuple: (i16, i16, i16)) -> Result { + if is_valid_rgb_value(tuple.0) && is_valid_rgb_value(tuple.1) && is_valid_rgb_value(tuple.2) + { + // Cannot loop through tuple, so we need to check each value separately + Ok(Color { + red: tuple.0 as u8, + green: tuple.1 as u8, + blue: tuple.2 as u8, + }) + } else { + Err(Self::Error::IntConversion) + } } } @@ -48,6 +62,18 @@ impl TryFrom<(i16, i16, i16)> for Color { impl TryFrom<[i16; 3]> for Color { type Error = IntoColorError; fn try_from(arr: [i16; 3]) -> Result { + // Iterate through the array and check if each value is in the 0..=255 range + // Use `all` method to check all values + // Function `is_valid_rgb_value` owns the value "c", so we need to use a reference "&c" + if arr.iter().all(|&c| is_valid_rgb_value(c)) { + Ok(Color { + red: arr[0] as u8, + green: arr[1] as u8, + blue: arr[2] as u8, + }) + } else { + Err(Self::Error::IntConversion) + } } } @@ -55,6 +81,20 @@ impl TryFrom<[i16; 3]> for Color { impl TryFrom<&[i16]> for Color { type Error = IntoColorError; fn try_from(slice: &[i16]) -> Result { + // Slice is a dynamic type, so we need to check the length + if slice.len() != 3 { + return Err(Self::Error::BadLen); + } + // Iterate through the slice and check if each value is in the 0..=255 range + if slice.iter().all(|&c| is_valid_rgb_value(c)) { + Ok(Color { + red: slice[0] as u8, + green: slice[1] as u8, + blue: slice[2] as u8, + }) + } else { + Err(Self::Error::IntConversion) + } } } diff --git a/exercises/23_conversions/5_as_ref_mut.rs b/exercises/23_conversions/5_as_ref_mut.rs index 2ba9e3f..2dc1263 100644 --- a/exercises/23_conversions/5_as_ref_mut.rs +++ b/exercises/23_conversions/5_as_ref_mut.rs @@ -8,24 +8,25 @@ // hint. // I AM NOT DONE +use std::convert::{AsMut, AsRef}; // Obtain the number of bytes (not characters) in the given argument. -// TODO: Add the AsRef trait appropriately as a trait bound. -fn byte_counter(arg: T) -> usize { +// Add the AsRef trait appropriately as a trait bound. +fn byte_counter>(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. -fn char_counter(arg: T) -> usize { +// Add the AsRef trait appropriately as a trait bound. +fn char_counter>(arg: T) -> usize { arg.as_ref().chars().count() } // Squares a number using as_mut(). -// TODO: Add the appropriate trait bound. -fn num_sq(arg: &mut T) { - // TODO: Implement the function body. - ??? +// Add the appropriate trait bound. +fn num_sq>(num: &mut T) { + let n = num.as_mut(); + *n = (*n) * (*n); } #[cfg(test)] diff --git a/exercises/23_conversions/README.md b/exercises/23_conversions/README.md index 619a78c..005fbbf 100644 --- a/exercises/23_conversions/README.md +++ b/exercises/23_conversions/README.md @@ -2,18 +2,19 @@ Rust offers a multitude of ways to convert a value of a given type into another type. -The simplest form of type conversion is a type cast expression. It is denoted with the binary operator `as`. For instance, `println!("{}", 1 + 1.0);` would not compile, since `1` is an integer while `1.0` is a float. However, `println!("{}", 1 as f32 + 1.0)` should compile. The exercise [`using_as`](using_as.rs) tries to cover this. +The simplest form of type conversion is a type cast expression. It is denoted with the binary operator `as`. For instance, `println!("{}", 1 + 1.0);` would not compile, since `1` is an integer while `1.0` is a float. However, `println!("{}", 1 as f32 + 1.0)` should compile. The exercise [`using_as`](1_using_as.rs) tries to cover this. Rust also offers traits that facilitate type conversions upon implementation. These traits can be found under the [`convert`](https://doc.rust-lang.org/std/convert/index.html) module. The traits are the following: -- `From` and `Into` covered in [`from_into`](from_into.rs) -- `TryFrom` and `TryInto` covered in [`try_from_into`](try_from_into.rs) -- `AsRef` and `AsMut` covered in [`as_ref_mut`](as_ref_mut.rs) +- `From` and `Into` covered in [`from_into`](2_from_into.rs) +- `FromStr` covered in [`from_str`](3_from_str.rs) +- `TryFrom` and `TryInto` covered in [`try_from_into`](4_try_from_into.rs) +- `AsRef` and `AsMut` covered in [`as_ref_mut`](5_as_ref_mut.rs) Furthermore, the `std::str` module offers a trait called [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) which helps with converting strings into target types via the `parse` method on strings. If properly implemented for a given type `Person`, then `let p: Person = "Mark,20".parse().unwrap()` should both compile and run without panicking. -These should be the main ways ***within the standard library*** to convert data into your desired types. +These should be the main ways **_within the standard library_** to convert data into your desired types. ## Further information