From 65cc4cf12c3f487901917b1941ce0421295142ad Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 17 Feb 2016 18:46:10 -0500 Subject: Add jleedev's try!, From, and Error exercise! ✨ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made some modifications to clarify the structure since this is a bigger example, also added some more hints. Name it errorsn since I think we could still use more exercises between errors1 and errorsn, but I've been sitting on these too long as it is :) --- error_handling/errorsn.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 error_handling/errorsn.rs (limited to 'error_handling/errorsn.rs') diff --git a/error_handling/errorsn.rs b/error_handling/errorsn.rs new file mode 100644 index 0000000..4b0d56e --- /dev/null +++ b/error_handling/errorsn.rs @@ -0,0 +1,119 @@ +// This is a bigger error exercise than the previous ones! +// +// Edit the `read_and_validate` function so that it compiles and +// passes the tests... so many things could go wrong! +// +// - Reading from stdin could produce an io::Error +// - Parsing the input could produce a num::ParseIntError +// - Validating the input could produce a CreationError (defined below) +// +// How can we lump these errors into one general error? That is, what +// type goes where the question marks are, and how do we return +// that type from the body of read_and_validate? +// +// Scroll down for hints :) + +use std::error; +use std::fmt; +use std::io; + +// PositiveNonzeroInteger is a struct defined below the tests. +fn read_and_validate(b: &mut io::BufRead) -> Result { + let mut line = String::new(); + b.read_line(&mut line); + let num: i64 = line.trim().parse(); + PositiveNonzeroInteger::new(num) +} + +// This is a test helper function that turns a &str into a BufReader. +fn test_with_str(s: &str) -> Result> { + let mut b = io::BufReader::new(s.as_bytes()); + read_and_validate(&mut b) +} + +#[test] +fn test_success() { + let x = test_with_str("42\n"); + assert_eq!(PositiveNonzeroInteger(42), x.unwrap()); +} + +#[test] +fn test_not_num() { + let x = test_with_str("eleven billion\n"); + assert!(x.is_err()); +} + +#[test] +fn test_non_positive() { + let x = test_with_str("-40\n"); + assert!(x.is_err()); +} + +#[test] +fn test_ioerror() { + struct Broken; + impl io::Read for Broken { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!")) + } + } + let mut b = io::BufReader::new(Broken); + assert!(read_and_validate(&mut b).is_err()); +} + +#[derive(PartialEq,Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + if value == 0 { + Err(CreationError::Zero) + } else if value < 0 { + Err(CreationError::Negative) + } else { + Ok(PositiveNonzeroInteger(value as u64)) + } + } +} + +#[test] +fn test_positive_nonzero_integer_creation() { + assert!(PositiveNonzeroInteger::new(10).is_ok()); + assert_eq!(Err(CreationError::Negative), PositiveNonzeroInteger::new(-10)); + assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); +} + +#[derive(PartialEq,Debug)] +enum CreationError { + Negative, + Zero, +} + +impl fmt::Display for CreationError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str((self as &error::Error).description()) + } +} + +impl error::Error for CreationError { + fn description(&self) -> &str { + match *self { + CreationError::Negative => "Negative", + CreationError::Zero => "Zero", + } + } +} + +// First hint: To figure out what type should go where the ??? is, take a look +// at the test helper function `test_with_str`, since it returns whatever +// `read_and_validate` returns and`test_with_str` has its signature fully +// specified. + +// Next hint: anywhere in `read_and_validate` that we call a function that +// returns a `Result`, wrap that call in a `try!` macro call. Use the compiler +// error messages and warnings to guide you to all the places you need to do +// this. You might need to rewrap some `try!` return values in a `Result::Ok`! + +// This works because under the hood, the `try!` macro calls `From::from` +// on the error value to convert it to a boxed trait object, a Box, +// which is polymorphic. -- cgit v1.2.3