From c21fb10ae4f341a1af146bee714acd7065f3ba54 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 16 Feb 2016 18:14:20 -0500 Subject: [PATCH] Add an exercise for rewriting Option to be Result In the case where you'd like to provide an explanation why the function isn't able to do the thing, rather than just not doing the thing. --- README.md | 2 +- error_handling/errors1.rs | 72 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 error_handling/errors1.rs diff --git a/README.md b/README.md index 82584f07..183a4ed0 100644 --- a/README.md +++ b/README.md @@ -96,8 +96,8 @@ Note that the exercises in this section may look similar to each other but they The [Error Handling](https://doc.rust-lang.org/stable/book/error-handling.html) and [Generics](https://doc.rust-lang.org/stable/book/generics.html) sections are relevant. - ["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) ### Standard library types diff --git a/error_handling/errors1.rs b/error_handling/errors1.rs new file mode 100644 index 00000000..843e6206 --- /dev/null +++ b/error_handling/errors1.rs @@ -0,0 +1,72 @@ +// This function refuses to generate text to be printed on a nametag if +// you pass it an empty string. It'd be nicer if it explained what the problem +// was, instead of just sometimes returning `None`. The 2nd test currently +// does not compile or pass, but it illustrates the behavior we would like +// this function to have. +// Scroll down for hints!!! + +pub fn generate_nametag_text(name: String) -> Option { + if name.len() > 0 { + Some(format!("Hi! My name is {}", name)) + } else { + // Empty names aren't allowed. + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // This test passes initially if you comment out the 2nd test. + // You'll need to update what this test expects when you change + // the function under test! + #[test] + fn generates_nametag_text_for_a_nonempty_name() { + assert_eq!( + generate_nametag_text("Beyoncé".into()), + Some("Hi! My name is Beyoncé".into()) + ); + } + + #[test] + fn explains_why_generating_nametag_text_fails() { + assert_eq!( + generate_nametag_text("".into()), + Err("`name` was empty; it must be nonempty.".into()) + ); + } +} + + + + + + + + + + + + + + + + + + + + +// `Err` is one of the variants of `Result`, so what the 2nd test is saying +// is that `generate_nametag_text` should return a `Result` instead of an +// `Option`. + +// To make this change, you'll need to: +// - update the return type in the function signature to be a Result that +// could be the variants `Ok(String)` and `Err(String)` +// - change the body of the function to return `Ok(stuff)` where it currently +// returns `Some(stuff)` +// - change the body of the function to return `Err(error message)` where it +// currently returns `None` +// - change the first test to expect `Ok(stuff)` where it currently expects +// `Some(stuff)`.