Merge pull request #781 from tlyu/advanced-errs
feature: advanced errors
This commit is contained in:
commit
c2ed98deb3
|
@ -0,0 +1,98 @@
|
||||||
|
// advanced_errs1.rs
|
||||||
|
|
||||||
|
// Remember back in errors6, we had multiple mapping functions so that we
|
||||||
|
// could translate lower-level errors into our custom error type using
|
||||||
|
// `map_err()`? What if we could use the `?` operator directly instead?
|
||||||
|
|
||||||
|
// Make this code compile! Execute `rustlings hint advanced_errs1` for
|
||||||
|
// hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
// This is a custom error type that we will be using in the `FromStr`
|
||||||
|
// implementation.
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum ParsePosNonzeroError {
|
||||||
|
Creation(CreationError),
|
||||||
|
ParseInt(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CreationError> for ParsePosNonzeroError {
|
||||||
|
fn from(e: CreationError) -> Self {
|
||||||
|
// TODO: complete this implementation so that the `?` operator will
|
||||||
|
// work for `CreationError`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement another instance of the `From` trait here so that the
|
||||||
|
// `?` operator will work in the other place in the `FromStr`
|
||||||
|
// implementation below.
|
||||||
|
|
||||||
|
// Don't change anything below this line.
|
||||||
|
|
||||||
|
impl FromStr for PositiveNonzeroInteger {
|
||||||
|
type Err = ParsePosNonzeroError;
|
||||||
|
fn from_str(s: &str) -> Result<PositiveNonzeroInteger, Self::Err> {
|
||||||
|
let x: i64 = s.parse()?;
|
||||||
|
Ok(PositiveNonzeroInteger::new(x)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum CreationError {
|
||||||
|
Negative,
|
||||||
|
Zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PositiveNonzeroInteger {
|
||||||
|
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
||||||
|
match value {
|
||||||
|
x if x < 0 => Err(CreationError::Negative),
|
||||||
|
x if x == 0 => Err(CreationError::Zero),
|
||||||
|
x => Ok(PositiveNonzeroInteger(x as u64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_error() {
|
||||||
|
// We can't construct a ParseIntError, so we have to pattern match.
|
||||||
|
assert!(matches!(
|
||||||
|
PositiveNonzeroInteger::from_str("not a number"),
|
||||||
|
Err(ParsePosNonzeroError::ParseInt(_))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_negative() {
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::from_str("-555"),
|
||||||
|
Err(ParsePosNonzeroError::Creation(CreationError::Negative))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero() {
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::from_str("0"),
|
||||||
|
Err(ParsePosNonzeroError::Creation(CreationError::Zero))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_positive() {
|
||||||
|
let x = PositiveNonzeroInteger::new(42);
|
||||||
|
assert!(x.is_ok());
|
||||||
|
assert_eq!(PositiveNonzeroInteger::from_str("42"), Ok(x.unwrap()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,203 @@
|
||||||
|
// advanced_errs2.rs
|
||||||
|
|
||||||
|
// This exercise demonstrates a few traits that are useful for custom error
|
||||||
|
// types to implement, especially so that other code can consume the custom
|
||||||
|
// error type more usefully.
|
||||||
|
|
||||||
|
// Make this compile, and make the tests pass!
|
||||||
|
// Execute `rustlings hint advanced_errs2` for hints.
|
||||||
|
|
||||||
|
// Steps:
|
||||||
|
// 1. Implement a missing trait so that `main()` will compile.
|
||||||
|
// 2. Complete the partial implementation of `From` for
|
||||||
|
// `ParseClimateError`.
|
||||||
|
// 3. Handle the missing error cases in the `FromStr` implementation for
|
||||||
|
// `Climate`.
|
||||||
|
// 4. Complete the partial implementation of `Display` for
|
||||||
|
// `ParseClimateError`.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
use std::num::{ParseFloatError, ParseIntError};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
// This is the custom error type that we will be using for the parser for
|
||||||
|
// `Climate`.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum ParseClimateError {
|
||||||
|
Empty,
|
||||||
|
BadLen,
|
||||||
|
NoCity,
|
||||||
|
ParseInt(ParseIntError),
|
||||||
|
ParseFloat(ParseFloatError),
|
||||||
|
}
|
||||||
|
|
||||||
|
// This `From` implementation allows the `?` operator to work on
|
||||||
|
// `ParseIntError` values.
|
||||||
|
impl From<ParseIntError> for ParseClimateError {
|
||||||
|
fn from(e: ParseIntError) -> Self {
|
||||||
|
Self::ParseInt(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This `From` implementation allows the `?` operator to work on
|
||||||
|
// `ParseFloatError` values.
|
||||||
|
impl From<ParseFloatError> for ParseClimateError {
|
||||||
|
fn from(e: ParseFloatError) -> Self {
|
||||||
|
// TODO: Complete this function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement a missing trait so that `main()` below will compile. It
|
||||||
|
// is not necessary to implement any methods inside the missing trait.
|
||||||
|
|
||||||
|
// The `Display` trait allows for other code to obtain the error formatted
|
||||||
|
// as a user-visible string.
|
||||||
|
impl Display for ParseClimateError {
|
||||||
|
// TODO: Complete this function so that it produces the correct strings
|
||||||
|
// for each error variant.
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
// Imports the variants to make the following code more compact.
|
||||||
|
use ParseClimateError::*;
|
||||||
|
match self {
|
||||||
|
NoCity => write!(f, "no city name"),
|
||||||
|
ParseFloat(e) => write!(f, "error parsing temperature: {}", e),
|
||||||
|
_ => write!(f, "unhandled error!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct Climate {
|
||||||
|
city: String,
|
||||||
|
year: u32,
|
||||||
|
temp: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for `Climate`.
|
||||||
|
// 1. Split the input string into 3 fields: city, year, temp.
|
||||||
|
// 2. Return an error if the string is empty or has the wrong number of
|
||||||
|
// fields.
|
||||||
|
// 3. Return an error if the city name is empty.
|
||||||
|
// 4. Parse the year as a `u32` and return an error if that fails.
|
||||||
|
// 5. Parse the temp as a `f32` and return an error if that fails.
|
||||||
|
// 6. Return an `Ok` value containing the completed `Climate` value.
|
||||||
|
impl FromStr for Climate {
|
||||||
|
type Err = ParseClimateError;
|
||||||
|
// TODO: Complete this function by making it handle the missing error
|
||||||
|
// cases.
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let v: Vec<_> = s.split(',').collect();
|
||||||
|
let (city, year, temp) = match &v[..] {
|
||||||
|
[city, year, temp] => (city.to_string(), year, temp),
|
||||||
|
_ => return Err(ParseClimateError::BadLen),
|
||||||
|
};
|
||||||
|
let year: u32 = year.parse()?;
|
||||||
|
let temp: f32 = temp.parse()?;
|
||||||
|
Ok(Climate { city, year, temp })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't change anything below this line (other than to enable ignored
|
||||||
|
// tests).
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
println!("{:?}", "Hong Kong,1999,25.7".parse::<Climate>()?);
|
||||||
|
println!("{:?}", "".parse::<Climate>()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_empty() {
|
||||||
|
let res = "".parse::<Climate>();
|
||||||
|
assert_eq!(res, Err(ParseClimateError::Empty));
|
||||||
|
assert_eq!(res.unwrap_err().to_string(), "empty input");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_short() {
|
||||||
|
let res = "Boston,1991".parse::<Climate>();
|
||||||
|
assert_eq!(res, Err(ParseClimateError::BadLen));
|
||||||
|
assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_long() {
|
||||||
|
let res = "Paris,1920,17.2,extra".parse::<Climate>();
|
||||||
|
assert_eq!(res, Err(ParseClimateError::BadLen));
|
||||||
|
assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_no_city() {
|
||||||
|
let res = ",1997,20.5".parse::<Climate>();
|
||||||
|
assert_eq!(res, Err(ParseClimateError::NoCity));
|
||||||
|
assert_eq!(res.unwrap_err().to_string(), "no city name");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_int_neg() {
|
||||||
|
let res = "Barcelona,-25,22.3".parse::<Climate>();
|
||||||
|
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
||||||
|
let err = res.unwrap_err();
|
||||||
|
if let ParseClimateError::ParseInt(ref inner) = err {
|
||||||
|
assert_eq!(
|
||||||
|
err.to_string(),
|
||||||
|
format!("error parsing year: {}", inner.to_string())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_int_bad() {
|
||||||
|
let res = "Beijing,foo,15.0".parse::<Climate>();
|
||||||
|
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
||||||
|
let err = res.unwrap_err();
|
||||||
|
if let ParseClimateError::ParseInt(ref inner) = err {
|
||||||
|
assert_eq!(
|
||||||
|
err.to_string(),
|
||||||
|
format!("error parsing year: {}", inner.to_string())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_float() {
|
||||||
|
let res = "Manila,2001,bar".parse::<Climate>();
|
||||||
|
assert!(matches!(res, Err(ParseClimateError::ParseFloat(_))));
|
||||||
|
let err = res.unwrap_err();
|
||||||
|
if let ParseClimateError::ParseFloat(ref inner) = err {
|
||||||
|
assert_eq!(
|
||||||
|
err.to_string(),
|
||||||
|
format!("error parsing temperature: {}", inner.to_string())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_good() {
|
||||||
|
let res = "Munich,2015,23.1".parse::<Climate>();
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
Ok(Climate {
|
||||||
|
city: "Munich".to_string(),
|
||||||
|
year: 2015,
|
||||||
|
temp: 23.1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_downcast() {
|
||||||
|
let res = "São Paulo,-21,28.5".parse::<Climate>();
|
||||||
|
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
||||||
|
let err = res.unwrap_err();
|
||||||
|
let inner: Option<&(dyn Error + 'static)> = err.source();
|
||||||
|
assert!(inner.is_some());
|
||||||
|
assert!(inner.unwrap().is::<ParseIntError>());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,31 @@
|
||||||
// This does practically the same thing that TryFrom<&str> does.
|
// from_str.rs
|
||||||
|
// This is similar to from_into.rs, 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
|
// Additionally, upon implementing FromStr, you can use the `parse` method
|
||||||
// on strings to generate an object of the implementor type.
|
// on strings to generate an object of the implementor type.
|
||||||
// You can read more about it at https://doc.rust-lang.org/std/str/trait.FromStr.html
|
// You can read more about it at https://doc.rust-lang.org/std/str/trait.FromStr.html
|
||||||
use std::error;
|
use std::num::ParseIntError;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: usize,
|
age: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We will use this error type for the `FromStr` implementation.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum ParsePersonError {
|
||||||
|
// Empty input string
|
||||||
|
Empty,
|
||||||
|
// Incorrect number of fields
|
||||||
|
BadLen,
|
||||||
|
// Empty name field
|
||||||
|
NoName,
|
||||||
|
// Wrapped error from parse::<usize>()
|
||||||
|
ParseInt(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
||||||
// Steps:
|
// Steps:
|
||||||
|
@ -24,7 +39,7 @@ struct Person {
|
||||||
// If everything goes well, then return a Result of a Person object
|
// If everything goes well, then return a Result of a Person object
|
||||||
|
|
||||||
impl FromStr for Person {
|
impl FromStr for Person {
|
||||||
type Err = Box<dyn error::Error>;
|
type Err = ParsePersonError;
|
||||||
fn from_str(s: &str) -> Result<Person, Self::Err> {
|
fn from_str(s: &str) -> Result<Person, Self::Err> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +55,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_input() {
|
fn empty_input() {
|
||||||
assert!("".parse::<Person>().is_err());
|
assert_eq!("".parse::<Person>(), Err(ParsePersonError::Empty));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn good_input() {
|
fn good_input() {
|
||||||
|
@ -52,41 +67,56 @@ mod tests {
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_age() {
|
fn missing_age() {
|
||||||
assert!("John,".parse::<Person>().is_err());
|
assert!(matches!(
|
||||||
|
"John,".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::ParseInt(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_age() {
|
fn invalid_age() {
|
||||||
assert!("John,twenty".parse::<Person>().is_err());
|
assert!(matches!(
|
||||||
|
"John,twenty".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::ParseInt(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_comma_and_age() {
|
fn missing_comma_and_age() {
|
||||||
assert!("John".parse::<Person>().is_err());
|
assert_eq!("John".parse::<Person>(), Err(ParsePersonError::BadLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_name() {
|
fn missing_name() {
|
||||||
assert!(",1".parse::<Person>().is_err());
|
assert_eq!(",1".parse::<Person>(), Err(ParsePersonError::NoName));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_name_and_age() {
|
fn missing_name_and_age() {
|
||||||
assert!(",".parse::<Person>().is_err());
|
assert!(matches!(
|
||||||
|
",".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_name_and_invalid_age() {
|
fn missing_name_and_invalid_age() {
|
||||||
assert!(",one".parse::<Person>().is_err());
|
assert!(matches!(
|
||||||
|
",one".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trailing_comma() {
|
fn trailing_comma() {
|
||||||
assert!("John,32,".parse::<Person>().is_err());
|
assert_eq!("John,32,".parse::<Person>(), Err(ParsePersonError::BadLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trailing_comma_and_some_string() {
|
fn trailing_comma_and_some_string() {
|
||||||
assert!("John,32,man".parse::<Person>().is_err());
|
assert_eq!(
|
||||||
|
"John,32,man".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::BadLen)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
// try_from_into.rs
|
||||||
// TryFrom is a simple and safe type conversion that may fail in a controlled way under some circumstances.
|
// 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
|
// Basically, this is the same as From. The main difference is that this should return a Result type
|
||||||
// instead of the target type itself.
|
// instead of the target type itself.
|
||||||
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::error;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct Color {
|
struct Color {
|
||||||
|
@ -12,12 +12,21 @@ struct Color {
|
||||||
blue: u8,
|
blue: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We will use this error type for these `TryFrom` conversions.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum IntoColorError {
|
||||||
|
// Incorrect length of slice
|
||||||
|
BadLen,
|
||||||
|
// Integer conversion error
|
||||||
|
IntConversion,
|
||||||
|
}
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
||||||
// Your task is to complete this implementation
|
// Your task is to complete this implementation
|
||||||
// and return an Ok result of inner type Color.
|
// and return an Ok result of inner type Color.
|
||||||
// You need to create an implementation for a tuple of three integers,
|
// You need to create an implementation for a tuple of three integers,
|
||||||
// an array of three integers and a slice of integers.
|
// an array of three integers, and a slice of integers.
|
||||||
//
|
//
|
||||||
// Note that the implementation for tuple and array will be checked at compile time,
|
// Note that the implementation for tuple and array will be checked at compile time,
|
||||||
// but the slice implementation needs to check the slice length!
|
// but the slice implementation needs to check the slice length!
|
||||||
|
@ -25,20 +34,23 @@ struct Color {
|
||||||
|
|
||||||
// Tuple implementation
|
// Tuple implementation
|
||||||
impl TryFrom<(i16, i16, i16)> for Color {
|
impl TryFrom<(i16, i16, i16)> for Color {
|
||||||
type Error = Box<dyn error::Error>;
|
type Error = IntoColorError;
|
||||||
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {}
|
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array implementation
|
// Array implementation
|
||||||
impl TryFrom<[i16; 3]> for Color {
|
impl TryFrom<[i16; 3]> for Color {
|
||||||
type Error = Box<dyn error::Error>;
|
type Error = IntoColorError;
|
||||||
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {}
|
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slice implementation
|
// Slice implementation
|
||||||
impl TryFrom<&[i16]> for Color {
|
impl TryFrom<&[i16]> for Color {
|
||||||
type Error = Box<dyn error::Error>;
|
type Error = IntoColorError;
|
||||||
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {}
|
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -46,15 +58,15 @@ fn main() {
|
||||||
let c1 = Color::try_from((183, 65, 14));
|
let c1 = Color::try_from((183, 65, 14));
|
||||||
println!("{:?}", c1);
|
println!("{:?}", c1);
|
||||||
|
|
||||||
// Since From is implemented for Color, we should be able to use Into
|
// Since TryFrom is implemented for Color, we should be able to use TryInto
|
||||||
let c2: Result<Color, _> = [183, 65, 14].try_into();
|
let c2: Result<Color, _> = [183, 65, 14].try_into();
|
||||||
println!("{:?}", c2);
|
println!("{:?}", c2);
|
||||||
|
|
||||||
let v = vec![183, 65, 14];
|
let v = vec![183, 65, 14];
|
||||||
// With slice we should use `from` function
|
// With slice we should use `try_from` function
|
||||||
let c3 = Color::try_from(&v[..]);
|
let c3 = Color::try_from(&v[..]);
|
||||||
println!("{:?}", c3);
|
println!("{:?}", c3);
|
||||||
// or take slice within round brackets and use Into
|
// or take slice within round brackets and use TryInto
|
||||||
let c4: Result<Color, _> = (&v[..]).try_into();
|
let c4: Result<Color, _> = (&v[..]).try_into();
|
||||||
println!("{:?}", c4);
|
println!("{:?}", c4);
|
||||||
}
|
}
|
||||||
|
@ -65,15 +77,24 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_out_of_range_positive() {
|
fn test_tuple_out_of_range_positive() {
|
||||||
assert!(Color::try_from((256, 1000, 10000)).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from((256, 1000, 10000)),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_out_of_range_negative() {
|
fn test_tuple_out_of_range_negative() {
|
||||||
assert!(Color::try_from((-1, -10, -256)).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from((-1, -10, -256)),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_sum() {
|
fn test_tuple_sum() {
|
||||||
assert!(Color::try_from((-1, 255, 255)).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from((-1, 255, 255)),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_correct() {
|
fn test_tuple_correct() {
|
||||||
|
@ -91,17 +112,17 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_out_of_range_positive() {
|
fn test_array_out_of_range_positive() {
|
||||||
let c: Result<Color, _> = [1000, 10000, 256].try_into();
|
let c: Result<Color, _> = [1000, 10000, 256].try_into();
|
||||||
assert!(c.is_err());
|
assert_eq!(c, Err(IntoColorError::IntConversion));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_out_of_range_negative() {
|
fn test_array_out_of_range_negative() {
|
||||||
let c: Result<Color, _> = [-10, -256, -1].try_into();
|
let c: Result<Color, _> = [-10, -256, -1].try_into();
|
||||||
assert!(c.is_err());
|
assert_eq!(c, Err(IntoColorError::IntConversion));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_sum() {
|
fn test_array_sum() {
|
||||||
let c: Result<Color, _> = [-1, 255, 255].try_into();
|
let c: Result<Color, _> = [-1, 255, 255].try_into();
|
||||||
assert!(c.is_err());
|
assert_eq!(c, Err(IntoColorError::IntConversion));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_correct() {
|
fn test_array_correct() {
|
||||||
|
@ -119,17 +140,26 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_out_of_range_positive() {
|
fn test_slice_out_of_range_positive() {
|
||||||
let arr = [10000, 256, 1000];
|
let arr = [10000, 256, 1000];
|
||||||
assert!(Color::try_from(&arr[..]).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from(&arr[..]),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_out_of_range_negative() {
|
fn test_slice_out_of_range_negative() {
|
||||||
let arr = [-256, -1, -10];
|
let arr = [-256, -1, -10];
|
||||||
assert!(Color::try_from(&arr[..]).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from(&arr[..]),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_sum() {
|
fn test_slice_sum() {
|
||||||
let arr = [-1, 255, 255];
|
let arr = [-1, 255, 255];
|
||||||
assert!(Color::try_from(&arr[..]).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from(&arr[..]),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_correct() {
|
fn test_slice_correct() {
|
||||||
|
@ -148,11 +178,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_excess_length() {
|
fn test_slice_excess_length() {
|
||||||
let v = vec![0, 0, 0, 0];
|
let v = vec![0, 0, 0, 0];
|
||||||
assert!(Color::try_from(&v[..]).is_err());
|
assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_insufficient_length() {
|
fn test_slice_insufficient_length() {
|
||||||
let v = vec![0, 0];
|
let v = vec![0, 0];
|
||||||
assert!(Color::try_from(&v[..]).is_err());
|
assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
95
info.toml
95
info.toml
|
@ -941,6 +941,27 @@ mode = "test"
|
||||||
hint = """
|
hint = """
|
||||||
Follow the steps provided right before the `From` implementation"""
|
Follow the steps provided right before the `From` implementation"""
|
||||||
|
|
||||||
|
[[exercises]]
|
||||||
|
name = "from_str"
|
||||||
|
path = "exercises/conversions/from_str.rs"
|
||||||
|
mode = "test"
|
||||||
|
hint = """
|
||||||
|
The implementation of FromStr should return an Ok with a Person object,
|
||||||
|
or an Err with an error if the string is not valid.
|
||||||
|
|
||||||
|
This is almost like the `from_into` exercise, but returning errors instead
|
||||||
|
of falling back to a default value.
|
||||||
|
|
||||||
|
Hint: Look at the test cases to see which error variants to return.
|
||||||
|
|
||||||
|
Another hint: You can use the `map_err` method of `Result` with a function
|
||||||
|
or a closure to wrap the error from `parse::<usize>`.
|
||||||
|
|
||||||
|
Yet another hint: If you would like to propagate errors by using the `?`
|
||||||
|
operator in your solution, you might want to look at
|
||||||
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
|
||||||
|
"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "try_from_into"
|
name = "try_from_into"
|
||||||
path = "exercises/conversions/try_from_into.rs"
|
path = "exercises/conversions/try_from_into.rs"
|
||||||
|
@ -949,17 +970,19 @@ hint = """
|
||||||
Follow the steps provided right before the `TryFrom` implementation.
|
Follow the steps provided right before the `TryFrom` implementation.
|
||||||
You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
||||||
|
|
||||||
You might want to look back at the exercise errors5 (or its hints) to remind
|
Hint: Is there an implementation of `TryFrom` in the standard library that
|
||||||
yourself about how `Box<dyn Error>` works.
|
can both do the required integer conversion and check the range of the input?
|
||||||
|
|
||||||
If you're trying to return a string as an error, note that neither `str`
|
Another hint: Look at the test cases to see which error variants to return.
|
||||||
nor `String` implements `error::Error`. However, there is an implementation
|
|
||||||
of `From<&str>` for `Box<dyn Error>`. This means you can use `.into()` or
|
|
||||||
the `?` operator to convert your string into the correct error type.
|
|
||||||
|
|
||||||
If you're having trouble with using the `?` operator to convert an error string,
|
Yet another hint: You can use the `map_err` or `or` methods of `Result` to
|
||||||
recall that `?` works to convert `Err(something)` into the appropriate error
|
convert errors.
|
||||||
type for returning from the function."""
|
|
||||||
|
Yet another hint: If you would like to propagate errors by using the `?`
|
||||||
|
operator in your solution, you might want to look at
|
||||||
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
|
||||||
|
|
||||||
|
Challenge: Can you make the `TryFrom` implementations generic over many integer types?"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "as_ref_mut"
|
name = "as_ref_mut"
|
||||||
|
@ -968,14 +991,54 @@ mode = "test"
|
||||||
hint = """
|
hint = """
|
||||||
Add AsRef<str> as a trait bound to the functions."""
|
Add AsRef<str> as a trait bound to the functions."""
|
||||||
|
|
||||||
|
# ADVANCED ERRORS
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "from_str"
|
name = "advanced_errs1"
|
||||||
path = "exercises/conversions/from_str.rs"
|
path = "exercises/advanced_errors/advanced_errs1.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
hint = """
|
hint = """
|
||||||
The implementation of FromStr should return an Ok with a Person object,
|
This exercise uses an updated version of the code in errors6. The parsing
|
||||||
or an Err with an error if the string is not valid.
|
code is now in an implementation of the `FromStr` trait. Note that the
|
||||||
This is almost like the `try_from_into` exercise.
|
parsing code uses `?` directly, without any calls to `map_err()`. There is
|
||||||
|
one partial implementation of the `From` trait example that you should
|
||||||
|
complete.
|
||||||
|
|
||||||
If you're having trouble with returning the correct error type, see the
|
Details: The `?` operator calls `From::from()` on the error type to convert
|
||||||
hints for try_from_into."""
|
it to the error type of the return type of the surrounding function.
|
||||||
|
|
||||||
|
Hint: You will need to write another implementation of `From` that has a
|
||||||
|
different input type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[exercises]]
|
||||||
|
name = "advanced_errs2"
|
||||||
|
path = "exercises/advanced_errors/advanced_errs2.rs"
|
||||||
|
mode = "test"
|
||||||
|
hint = """
|
||||||
|
This exercise demonstrates a few traits that are useful for custom error
|
||||||
|
types to implement. These traits make it easier for other code to consume
|
||||||
|
the custom error type.
|
||||||
|
|
||||||
|
Follow the steps in the comment near the top of the file. You will have to
|
||||||
|
supply a missing trait implementation, and complete a few incomplete ones.
|
||||||
|
|
||||||
|
You may find these pages to be helpful references:
|
||||||
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/define_error_type.html
|
||||||
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
|
||||||
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/wrap_error.html
|
||||||
|
|
||||||
|
Hint: What trait must our error type have for `main()` to return the return
|
||||||
|
type that it returns?
|
||||||
|
|
||||||
|
Another hint: It's not necessary to implement any methods inside the missing
|
||||||
|
trait. (Some methods have default implementations that are supplied by the
|
||||||
|
trait.)
|
||||||
|
|
||||||
|
Another hint: Consult the tests to determine which error variants (and which
|
||||||
|
error message text) to produce for certain error conditions.
|
||||||
|
|
||||||
|
Challenge: There is one test that is marked `#[ignore]`. Can you supply the
|
||||||
|
missing code that will make it pass? You may want to consult the standard
|
||||||
|
library documentation for a certain trait for more hints.
|
||||||
|
"""
|
||||||
|
|
Loading…
Reference in New Issue