Add jleedev's try!, From, and Error exercise!

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 :)
This commit is contained in:
Carol (Nichols || Goulding) 2016-02-17 18:46:10 -05:00
parent c21fb10ae4
commit 65cc4cf12c
2 changed files with 120 additions and 0 deletions

View File

@ -98,6 +98,7 @@ The [Error Handling](https://doc.rust-lang.org/stable/book/error-handling.html)
- ["option1.rs"](http://play.rust-lang.org/?code=%2F%2F+This+example+panics+because+the+second+time+it+calls+%60pop%60%2C+the+%60vec%60%0A%2F%2F+is+empty%2C+so+%60pop%60+returns+%60None%60%2C+and+%60unwrap%60+panics+if+it%27s+called%0A%2F%2F+on+%60None%60.+Handle+this+in+a+more+graceful+way+than+calling+%60unwrap%60%21%0A%2F%2F+Scroll+down+for+hints+%3A%29%0A%0Afn+main%28%29+%7B%0A++++let+mut+list+%3D+vec%21%5B3%5D%3B%0A%0A++++let+last+%3D+list.pop%28%29.unwrap%28%29%3B%0A++++println%21%28%22The+last+item+in+the+list+is+%7B%3A%3F%7D%22%2C+last%29%3B%0A%0A++++let+second_to_last+%3D+list.pop%28%29.unwrap%28%29%3B%0A++++println%21%28%22The+second-to-last+item+in+the+list+is+%7B%3A%3F%7D%22%2C+second_to_last%29%3B%0A%7D%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%2F%2F+Try+using+a+%60match%60+statement+where+the+arms+are+%60Some%28thing%29%60+and+%60None%60.%0A%2F%2F+Or+set+a+default+value+to+print+out+if+you+get+%60None%60+by+using+the%0A%2F%2F+function+%60unwrap_or%60.%0A%2F%2F+Or+use+an+%60if+let%60+statement+on+the+result+of+%60pop%28%29%60+to+both+destructure%0A%2F%2F+a+%60Some%60+value+and+only+print+out+something+if+we+have+a+value%21%0A)
- ["result1.rs"](http://play.rust-lang.org/?code=%2F%2F+Make+this+test+pass%21+Scroll+down+for+hints+%3A%29%0A%0A%23%5Bderive%28PartialEq%2CDebug%29%5D%0Astruct+PositiveNonzeroInteger%28u64%29%3B%0A%0A%23%5Bderive%28PartialEq%2CDebug%29%5D%0Aenum+CreationError+%7B%0A++++Negative%2C%0A++++Zero%2C%0A%7D%0A%0Aimpl+PositiveNonzeroInteger+%7B%0A++++fn+new%28value%3A+i64%29+-%3E+Result%3CPositiveNonzeroInteger%2C+CreationError%3E+%7B%0A++++++++Ok%28PositiveNonzeroInteger%28value+as+u64%29%29%0A++++%7D%0A%7D%0A%0A%23%5Btest%5D%0Afn+test_creation%28%29+%7B%0A++++assert%21%28PositiveNonzeroInteger%3A%3Anew%2810%29.is_ok%28%29%29%3B%0A++++assert_eq%21%28Err%28CreationError%3A%3ANegative%29%2C+PositiveNonzeroInteger%3A%3Anew%28-10%29%29%3B%0A++++assert_eq%21%28Err%28CreationError%3A%3AZero%29%2C+PositiveNonzeroInteger%3A%3Anew%280%29%29%3B%0A%7D%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%2F%2F+%60PositiveNonzeroInteger%3A%3Anew%60+is+always+creating+a+new+instance+and+returning+an+%60Ok%60+result.%0A%2F%2F+It+should+be+doing+some+checking%2C+returning+an+%60Err%60+result+if+those+checks+fail%2C+and+only%0A%2F%2F+returning+an+%60Ok%60+result+if+those+checks+determine+that+everything+is...+okay+%3A%29%0A)
- ["errors1.rs"](http://play.rust-lang.org/?code=%2F%2F+This+function+refuses+to+generate+text+to+be+printed+on+a+nametag+if%0A%2F%2F+you+pass+it+an+empty+string.+It%27d+be+nicer+if+it+explained+what+the+problem%0A%2F%2F+was%2C+instead+of+just+sometimes+returning+%60None%60.+The+2nd+test+currently%0A%2F%2F+does+not+compile+or+pass%2C+but+it+illustrates+the+behavior+we+would+like%0A%2F%2F+this+function+to+have.%0A%2F%2F+Scroll+down+for+hints%21%21%21%0A%0Apub+fn+generate_nametag_text%28name%3A+String%29+-%3E+Option%3CString%3E+%7B%0A++++if+name.len%28%29+%3E+0+%7B%0A++++++++Some%28format%21%28%22Hi%21+My+name+is+%7B%7D%22%2C+name%29%29%0A++++%7D+else+%7B%0A++++++++%2F%2F+Empty+names+aren%27t+allowed.%0A++++++++None%0A++++%7D%0A%7D%0A%0A%23%5Bcfg%28test%29%5D%0Amod+tests+%7B%0A++++use+super%3A%3A*%3B%0A%0A++++%2F%2F+This+test+passes+initially+if+you+comment+out+the+2nd+test.%0A++++%2F%2F+You%27ll+need+to+update+what+this+test+expects+when+you+change%0A++++%2F%2F+the+function+under+test%21%0A++++%23%5Btest%5D%0A++++fn+generates_nametag_text_for_a_nonempty_name%28%29+%7B%0A++++++++assert_eq%21%28%0A++++++++++++generate_nametag_text%28%22Beyonc%C3%A9%22.into%28%29%29%2C%0A++++++++++++Some%28%22Hi%21+My+name+is+Beyonc%C3%A9%22.into%28%29%29%0A++++++++%29%3B%0A++++%7D%0A%0A++++%23%5Btest%5D%0A++++fn+explains_why_generating_nametag_text_fails%28%29+%7B%0A++++++++assert_eq%21%28%0A++++++++++++generate_nametag_text%28%22%22.into%28%29%29%2C%0A++++++++++++Err%28%22%60name%60+was+empty%3B+it+must+be+nonempty.%22.into%28%29%29%0A++++++++%29%3B%0A++++%7D%0A%7D%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%2F%2F+%60Err%60+is+one+of+the+variants+of+%60Result%60%2C+so+what+the+2nd+test+is+saying%0A%2F%2F+is+that+%60generate_nametag_text%60+should+return+a+%60Result%60+instead+of+an%0A%2F%2F+%60Option%60.%0A%0A%2F%2F+To+make+this+change%2C+you%27ll+need+to%3A%0A%2F%2F+-+update+the+return+type+in+the+function+signature+to+be+a+Result+that%0A%2F%2F+++could+be+the+variants+%60Ok%28String%29%60+and+%60Err%28String%29%60%0A%2F%2F+-+change+the+body+of+the+function+to+return+%60Ok%28stuff%29%60+where+it+currently%0A%2F%2F+++returns+%60Some%28stuff%29%60%0A%2F%2F+-+change+the+body+of+the+function+to+return+%60Err%28error+message%29%60+where+it%0A%2F%2F+++currently+returns+%60None%60%0A%2F%2F+-+change+the+first+test+to+expect+%60Ok%28stuff%29%60+where+it+currently+expects%0A%2F%2F+++%60Some%28stuff%29%60.%0A)
- ["errorsn.rs"](http://play.rust-lang.org/?code=%2F%2F+This+is+a+bigger+error+exercise+than+the+previous+ones%21%0A%2F%2F%0A%2F%2F+Edit+the+%60read_and_validate%60+function+so+that+it+compiles+and%0A%2F%2F+passes+the+tests...+so+many+things+could+go+wrong%21%0A%2F%2F%0A%2F%2F+-+Reading+from+stdin+could+produce+an+io%3A%3AError%0A%2F%2F+-+Parsing+the+input+could+produce+a+num%3A%3AParseIntError%0A%2F%2F+-+Validating+the+input+could+produce+a+CreationError+%28defined+below%29%0A%2F%2F%0A%2F%2F+How+can+we+lump+these+errors+into+one+general+error%3F+That+is%2C+what%0A%2F%2F+type+goes+where+the+question+marks+are%2C+and+how+do+we+return%0A%2F%2F+that+type+from+the+body+of+read_and_validate%3F%0A%2F%2F%0A%2F%2F+Scroll+down+for+hints+%3A%29%0A%0Ause+std%3A%3Aerror%3B%0Ause+std%3A%3Afmt%3B%0Ause+std%3A%3Aio%3B%0A%0A%2F%2F+PositiveNonzeroInteger+is+a+struct+defined+below+the+tests.%0Afn+read_and_validate%28b%3A+%26mut+io%3A%3ABufRead%29+-%3E+Result%3CPositiveNonzeroInteger%2C+%3F%3F%3F%3E+%7B%0A++++let+mut+line+%3D+String%3A%3Anew%28%29%3B%0A++++b.read_line%28%26mut+line%29%3B%0A++++let+num%3A+i64+%3D+line.trim%28%29.parse%28%29%3B%0A++++PositiveNonzeroInteger%3A%3Anew%28num%29%0A%7D%0A%0A%2F%2F+This+is+a+test+helper+function+that+turns+a+%26str+into+a+BufReader.%0Afn+test_with_str%28s%3A+%26str%29+-%3E+Result%3CPositiveNonzeroInteger%2C+Box%3Cerror%3A%3AError%3E%3E+%7B%0A++++let+mut+b+%3D+io%3A%3ABufReader%3A%3Anew%28s.as_bytes%28%29%29%3B%0A++++read_and_validate%28%26mut+b%29%0A%7D%0A%0A%23%5Btest%5D%0Afn+test_success%28%29+%7B%0A++++let+x+%3D+test_with_str%28%2242%5Cn%22%29%3B%0A++++assert_eq%21%28PositiveNonzeroInteger%2842%29%2C+x.unwrap%28%29%29%3B%0A%7D%0A%0A%23%5Btest%5D%0Afn+test_not_num%28%29+%7B%0A++++let+x+%3D+test_with_str%28%22eleven+billion%5Cn%22%29%3B%0A++++assert%21%28x.is_err%28%29%29%3B%0A%7D%0A%0A%23%5Btest%5D%0Afn+test_non_positive%28%29+%7B%0A++++let+x+%3D+test_with_str%28%22-40%5Cn%22%29%3B%0A++++assert%21%28x.is_err%28%29%29%3B%0A%7D%0A%0A%23%5Btest%5D%0Afn+test_ioerror%28%29+%7B%0A++++struct+Broken%3B%0A++++impl+io%3A%3ARead+for+Broken+%7B%0A++++++++fn+read%28%26mut+self%2C+_buf%3A+%26mut+%5Bu8%5D%29+-%3E+io%3A%3AResult%3Cusize%3E+%7B%0A++++++++++++Err%28io%3A%3AError%3A%3Anew%28io%3A%3AErrorKind%3A%3ABrokenPipe%2C+%22uh-oh%21%22%29%29%0A++++++++%7D%0A++++%7D%0A++++let+mut+b+%3D+io%3A%3ABufReader%3A%3Anew%28Broken%29%3B%0A++++assert%21%28read_and_validate%28%26mut+b%29.is_err%28%29%29%3B%0A%7D%0A%0A%23%5Bderive%28PartialEq%2CDebug%29%5D%0Astruct+PositiveNonzeroInteger%28u64%29%3B%0A%0Aimpl+PositiveNonzeroInteger+%7B%0A++++fn+new%28value%3A+i64%29+-%3E+Result%3CPositiveNonzeroInteger%2C+CreationError%3E+%7B%0A++++++++if+value+%3D%3D+0+%7B%0A++++++++++++Err%28CreationError%3A%3AZero%29%0A++++++++%7D+else+if+value+%3C+0+%7B%0A++++++++++++Err%28CreationError%3A%3ANegative%29%0A++++++++%7D+else+%7B%0A++++++++++++Ok%28PositiveNonzeroInteger%28value+as+u64%29%29%0A++++++++%7D%0A++++%7D%0A%7D%0A%0A%23%5Btest%5D%0Afn+test_positive_nonzero_integer_creation%28%29+%7B%0A++++assert%21%28PositiveNonzeroInteger%3A%3Anew%2810%29.is_ok%28%29%29%3B%0A++++assert_eq%21%28Err%28CreationError%3A%3ANegative%29%2C+PositiveNonzeroInteger%3A%3Anew%28-10%29%29%3B%0A++++assert_eq%21%28Err%28CreationError%3A%3AZero%29%2C+PositiveNonzeroInteger%3A%3Anew%280%29%29%3B%0A%7D%0A%0A%23%5Bderive%28PartialEq%2CDebug%29%5D%0Aenum+CreationError+%7B%0A++++Negative%2C%0A++++Zero%2C%0A%7D%0A%0Aimpl+fmt%3A%3ADisplay+for+CreationError+%7B%0A++++fn+fmt%28%26self%2C+f%3A+%26mut+fmt%3A%3AFormatter%29+-%3E+fmt%3A%3AResult+%7B%0A++++++++f.write_str%28%28self+as+%26error%3A%3AError%29.description%28%29%29%0A++++%7D%0A%7D%0A%0Aimpl+error%3A%3AError+for+CreationError+%7B%0A++++fn+description%28%26self%29+-%3E+%26str+%7B%0A++++++++match+*self+%7B%0A++++++++++++CreationError%3A%3ANegative+%3D%3E+%22Negative%22%2C%0A++++++++++++CreationError%3A%3AZero+%3D%3E+%22Zero%22%2C%0A++++++++%7D%0A++++%7D%0A%7D%0A%0A%2F%2F+First+hint%3A+To+figure+out+what+type+should+go+where+the+%3F%3F%3F+is%2C+take+a+look%0A%2F%2F+at+the+test+helper+function+%60test_with_str%60%2C+since+it+returns+whatever%0A%2F%2F+%60read_and_validate%60+returns+and%60test_with_str%60+has+its+signature+fully%0A%2F%2F+specified.%0A%0A%2F%2F+Next+hint%3A+anywhere+in+%60read_and_validate%60+that+we+call+a+function+that%0A%2F%2F+returns+a+%60Result%60%2C+wrap+that+call+in+a+%60try%21%60+macro+call.+Use+the+compiler%0A%2F%2F+error+messages+and+warnings+to+guide+you+to+all+the+places+you+need+to+do%0A%2F%2F+this.+You+might+need+to+rewrap+some+%60try%21%60+return+values+in+a+%60Result%3A%3AOk%60%21%0A%0A%2F%2F+This+works+because+under+the+hood%2C+the+%60try%21%60+macro+calls+%60From%3A%3Afrom%60%0A%2F%2F+on+the+error+value+to+convert+it+to+a+boxed+trait+object%2C+a+Box%3Cerror%3A%3AError%3E%2C%0A%2F%2F+which+is+polymorphic.%0A)
### Standard library types

119
error_handling/errorsn.rs Normal file
View File

@ -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<PositiveNonzeroInteger, ???> {
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<PositiveNonzeroInteger, Box<error::Error>> {
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<usize> {
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<PositiveNonzeroInteger, CreationError> {
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<error::Error>,
// which is polymorphic.