赞
踩
// 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: u8, } // 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() -> Self { Self { name: String::from("John"), age: 30, } } } // 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. 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) -> Self { let (name, age) = match s.split_once(',') { Some((name, age)) => (name.trim(), age.trim()), _ => return Person::default(), }; if let Ok(age) = age.parse::<usize>() { if !name.is_empty() { return Person { name: String::from(name), age:age.try_into().unwrap(), }; } } Person::default() } } fn main() { // Use the `from` function. let p1 = Person::from("Mark,20"); println!("{p1:?}"); // Since `From` is implemented for Person, we are able to use `Into`. let p2: Person = "Gerald,70".into(); println!("{p2:?}"); } #[cfg(test)] mod tests { use super::*; #[test] fn test_default() { let dp = Person::default(); assert_eq!(dp.name, "John"); assert_eq!(dp.age, 30); } #[test] fn test_bad_convert() { let p = Person::from(""); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } #[test] fn test_good_convert() { let p = Person::from("Mark,20"); assert_eq!(p.name, "Mark"); assert_eq!(p.age, 20); } #[test] fn test_bad_age() { let p = Person::from("Mark,twenty"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } #[test] fn test_missing_comma_and_age() { let p: Person = Person::from("Mark"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } #[test] fn test_missing_age() { let p: Person = Person::from("Mark,"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } #[test] fn test_missing_name() { let p: Person = Person::from(",1"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } #[test] fn test_missing_name_and_age() { let p: Person = Person::from(","); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } #[test] fn test_missing_name_and_invalid_age() { let p: Person = Person::from(",one"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } #[test] fn test_trailing_comma() { let p: Person = Person::from("Mike,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,dog"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } }
// 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 use std::num::ParseIntError; use std::str::FromStr; #[derive(Debug, PartialEq)] struct Person { name: String, age: u8, } // We will use this error type for the `FromStr` implementation. #[derive(Debug, PartialEq)] enum ParsePersonError { // Incorrect number of fields BadLen, // Empty name field NoName, // Wrapped error from parse::<u8>() ParseInt(ParseIntError), } // 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. 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<Self, Self::Err> { if s.is_empty() { return Err(ParsePersonError::BadLen); } let splitted_item = s.split(',').collect::<Vec<&str>>(); let (name, age) = match &splitted_item[..] { [name, age] => ( name.to_string(), age.parse().map_err(ParsePersonError::ParseInt)?, ), _ => return Err(ParsePersonError::BadLen), }; if name.is_empty() { return Err(ParsePersonError::NoName); } Ok(Person { name, age, }) } } fn main() { 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(BadLen)); } #[test] fn good_input() { let p = "John,32".parse::<Person>(); assert!(p.is_ok()); let p = p.unwrap(); assert_eq!(p.name, "John"); assert_eq!(p.age, 32); } #[test] fn missing_age() { assert!(matches!("John,".parse::<Person>(), Err(ParseInt(_)))); } #[test] fn invalid_age() { assert!(matches!("John,twenty".parse::<Person>(), Err(ParseInt(_)))); } #[test] fn missing_comma_and_age() { assert_eq!("John".parse::<Person>(), Err(BadLen)); } #[test] fn missing_name() { assert_eq!(",1".parse::<Person>(), Err(NoName)); } #[test] fn missing_name_and_age() { assert!(matches!(",".parse::<Person>(), Err(NoName | ParseInt(_)))); } #[test] fn missing_name_and_invalid_age() { assert!(matches!( ",one".parse::<Person>(), Err(NoName | ParseInt(_)), )); } #[test] fn trailing_comma() { assert_eq!("John,32,".parse::<Person>(), Err(BadLen)); } #[test] fn trailing_comma_and_some_string() { assert_eq!("John,32,man".parse::<Person>(), Err(BadLen)); } }
// 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. // Obtain the number of bytes (not characters) in the given argument. // TODO: Add the `AsRef` trait appropriately as a trait bound. 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. // TODO: Add the `AsRef` trait appropriately as a trait bound. fn char_counter<T: AsRef<str>>(arg: T) -> usize { arg.as_ref().chars().count() } // Squares a number using `as_mut()`. // TODO: Add the appropriate trait bound. fn num_sq<T: AsMut<u32>>(arg: &mut T) { // TODO: Implement the function body. *arg.as_mut() *= *arg.as_mut() } fn main() { // You can optionally experiment here. } #[cfg(test)] mod tests { use super::*; #[test] fn different_counts() { let s = "Café au lait"; assert_ne!(char_counter(s), byte_counter(s)); } #[test] fn same_counts() { let s = "Cafe au lait"; assert_eq!(char_counter(s), byte_counter(s)); } #[test] fn different_counts_using_string() { let s = String::from("Café au lait"); assert_ne!(char_counter(s.clone()), byte_counter(s)); } #[test] fn same_counts_using_string() { let s = String::from("Cafe au lait"); assert_eq!(char_counter(s.clone()), byte_counter(s)); } #[test] fn mut_box() { let mut num: Box<u32> = Box::new(3); num_sq(&mut num); assert_eq!(*num, 9); } }
// `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 #![allow(clippy::useless_vec)] use std::convert::{TryFrom, TryInto}; #[derive(Debug, PartialEq)] struct Color { red: u8, green: u8, blue: u8, } // We will use this error type for the `TryFrom` conversions. #[derive(Debug, PartialEq)] enum IntoColorError { // Incorrect length of slice BadLen, // Integer conversion error IntConversion, } // 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> { let (red, green, blue) = tuple; for color in [red, green, blue] { if !(0..=255).contains(&color) { return Err(IntoColorError::IntConversion); } } Ok(Self { red: tuple.0 as u8, green: tuple.1 as u8, blue: tuple.2 as u8, }) } } // TODO: Array implementation. impl TryFrom<[i16; 3]> for Color { type Error = IntoColorError; fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> { for color in arr { if !(0..=255).contains(&color) { return Err(IntoColorError::IntConversion); } } Ok(Self { red: arr[0] as u8, green: arr[1] as u8, blue: arr[2] as u8, }) } } // 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> { if slice.len() != 3 { return Err(IntoColorError::BadLen); } for color in slice { if !(0..=255).contains(color) { return Err(IntoColorError::IntConversion); } } Ok(Self { red: slice[0] as u8, green: slice[1] as u8, blue: slice[2] as u8, }) } } fn main() { // Using the `try_from` function. let c1 = Color::try_from((183, 65, 14)); println!("{c1:?}"); // Since `TryFrom` is implemented for `Color`, we can use `TryInto`. let c2: Result<Color, _> = [183, 65, 14].try_into(); println!("{c2:?}"); let v = vec![183, 65, 14]; // With slice we should use the `try_from` function let c3 = Color::try_from(&v[..]); println!("{c3:?}"); // or put the slice within round brackets and use `try_into`. let c4: Result<Color, _> = (&v[..]).try_into(); 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(IntConversion)); } #[test] fn test_tuple_out_of_range_negative() { assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion)); } #[test] fn test_tuple_sum() { assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion)); } #[test] fn test_tuple_correct() { let c: Result<Color, _> = (183, 65, 14).try_into(); assert!(c.is_ok()); assert_eq!( c.unwrap(), Color { red: 183, green: 65, blue: 14, } ); } #[test] fn test_array_out_of_range_positive() { let c: Result<Color, _> = [1000, 10000, 256].try_into(); 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(IntConversion)); } #[test] fn test_array_sum() { let c: Result<Color, _> = [-1, 255, 255].try_into(); assert_eq!(c, Err(IntConversion)); } #[test] fn test_array_correct() { let c: Result<Color, _> = [183, 65, 14].try_into(); assert!(c.is_ok()); assert_eq!( c.unwrap(), Color { red: 183, green: 65, blue: 14 } ); } #[test] fn test_slice_out_of_range_positive() { let arr = [10000, 256, 1000]; 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(IntConversion)); } #[test] fn test_slice_sum() { let arr = [-1, 255, 255]; assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } #[test] fn test_slice_correct() { let v = vec![183, 65, 14]; let c: Result<Color, _> = Color::try_from(&v[..]); assert!(c.is_ok()); assert_eq!( c.unwrap(), Color { red: 183, green: 65, blue: 14, } ); } #[test] fn test_slice_excess_length() { let v = vec![0, 0, 0, 0]; 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(BadLen)); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。