diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 29a557a019..4f6c7424a7 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -118,6 +118,8 @@ bin = [ { name = "generics2_sol", path = "../solutions/14_generics/generics2.rs" }, { name = "traits1", path = "../exercises/15_traits/traits1.rs" }, { name = "traits1_sol", path = "../solutions/15_traits/traits1.rs" }, + { name = "errors7", path = "../exercises/13_error_handling/errors7.rs" }, + { name = "errors7_sol", path = "../solutions/13_error_handling/errors7.rs" }, { name = "traits2", path = "../exercises/15_traits/traits2.rs" }, { name = "traits2_sol", path = "../solutions/15_traits/traits2.rs" }, { name = "traits3", path = "../exercises/15_traits/traits3.rs" }, diff --git a/exercises/13_error_handling/errors7.rs b/exercises/13_error_handling/errors7.rs new file mode 100644 index 0000000000..5e401619d6 --- /dev/null +++ b/exercises/13_error_handling/errors7.rs @@ -0,0 +1,100 @@ +// While defining and using a custom error type in library code is recomended +// the ergonomics of the map_err approach presented in errors6 is suboptimal. +// The growing codebase could quickly become obfuscated by error conversions +// sprinkled here and there. Fortunetly, using traits we just learned about, +// we can use one more Rust feature to fix that. + +use std::num::ParseIntError; + +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// A custom error type that we will be using in `PositiveNonzeroInteger::parse`. +#[derive(PartialEq, Debug)] +enum ParsePosNonzeroError { + Creation(CreationError), + ParseInt(ParseIntError), +} + +impl ParsePosNonzeroError { + fn from_creation(err: CreationError) -> Self { + Self::Creation(err) + } + + fn from_parse_int(err: ParseIntError) -> Self { + Self::ParseInt(err) + } +} + +// TODO Implement From trait for ParseIntError +// impl From for ParsePosNonzeroError { +// fn from(err: ParseIntError) -> Self { +// ??? +// } +// } + +// TODO Implement From trait for CreationError +// ... + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + x if x < 0 => Err(CreationError::Negative), + 0 => Err(CreationError::Zero), + x => Ok(Self(x as u64)), + } + } + + fn parse(s: &str) -> Result { + // Don't change this line + let x: i64 = s.parse()?; + // Don't change this line + Ok(Self::new(x)?) + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_parse_error() { + assert!(matches!( + PositiveNonzeroInteger::parse("not a number"), + Err(ParsePosNonzeroError::ParseInt(_)), + )); + } + + #[test] + fn test_negative() { + assert_eq!( + PositiveNonzeroInteger::parse("-555"), + Err(ParsePosNonzeroError::Creation(CreationError::Negative)), + ); + } + + #[test] + fn test_zero() { + assert_eq!( + PositiveNonzeroInteger::parse("0"), + Err(ParsePosNonzeroError::Creation(CreationError::Zero)), + ); + } + + #[test] + fn test_positive() { + let x = PositiveNonzeroInteger::new(42).unwrap(); + assert_eq!(x.0, 42); + assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x)); + } +} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index e705598187..54705b617b 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -756,6 +756,13 @@ https://doc.rust-lang.org/book/ch10-02-traits.html The `+` operator can concatenate a `String` with a `&str`.""" +[[exercises]] +name = "errors7" +dir = "13_error_handling" +hint = """ +More about relation between try operator and the From trait: +https://doc.rust-lang.org/std/convert/trait.From.html#examples""" + [[exercises]] name = "traits2" dir = "15_traits" diff --git a/solutions/13_error_handling/errors7.rs b/solutions/13_error_handling/errors7.rs new file mode 100644 index 0000000000..b61e657d64 --- /dev/null +++ b/solutions/13_error_handling/errors7.rs @@ -0,0 +1,102 @@ +// While defining and using a custom error type in library code is recomended +// the ergonomics of the map_err approach presented in errors6 is suboptimal. +// The growing codebase could quickly become obfuscated by error conversions +// sprinkled here and there. Fortunetly, using traits we just learned about, +// we can use one more Rust feature to fix that. + +use std::num::ParseIntError; + +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// A custom error type that we will be using in `PositiveNonzeroInteger::parse`. +#[derive(PartialEq, Debug)] +enum ParsePosNonzeroError { + Creation(CreationError), + ParseInt(ParseIntError), +} + +impl ParsePosNonzeroError { + fn from_creation(err: CreationError) -> Self { + Self::Creation(err) + } + + fn from_parse_int(err: ParseIntError) -> Self { + Self::ParseInt(err) + } +} + +impl From for ParsePosNonzeroError { + fn from(err: ParseIntError) -> Self { + ParsePosNonzeroError::from_parse_int(err) + } +} + +impl From for ParsePosNonzeroError { + fn from(err: CreationError) -> Self { + ParsePosNonzeroError::from_creation(err) + } +} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + x if x < 0 => Err(CreationError::Negative), + 0 => Err(CreationError::Zero), + x => Ok(Self(x as u64)), + } + } + + fn parse(s: &str) -> Result { + // Don't change this line + let x: i64 = s.parse()?; + // Don't change this line + Ok(Self::new(x)?) + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_parse_error() { + assert!(matches!( + PositiveNonzeroInteger::parse("not a number"), + Err(ParsePosNonzeroError::ParseInt(_)), + )); + } + + #[test] + fn test_negative() { + assert_eq!( + PositiveNonzeroInteger::parse("-555"), + Err(ParsePosNonzeroError::Creation(CreationError::Negative)), + ); + } + + #[test] + fn test_zero() { + assert_eq!( + PositiveNonzeroInteger::parse("0"), + Err(ParsePosNonzeroError::Creation(CreationError::Zero)), + ); + } + + #[test] + fn test_positive() { + let x = PositiveNonzeroInteger::new(42).unwrap(); + assert_eq!(x.0, 42); + assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x)); + } +}