···44// construct to `Option` that can be used to express error conditions. Change
55// the function signature and body to return `Result<String, String>` instead
66// of `Option<String>`.
77-fn generate_nametag_text(name: String) -> Option<String> {
77+fn generate_nametag_text(name: String) -> Result<String, String> {
88 if name.is_empty() {
99 // Empty names aren't allowed
1010- None
1010+ Err(String::from("Empty names aren't allowed"))
1111 } else {
1212- Some(format!("Hi! My name is {name}"))
1212+ Ok(format!("Hi! My name is {name}"))
1313 }
1414}
1515
+1-1
exercises/13_error_handling/errors2.rs
···2121 let cost_per_item = 5;
22222323 // TODO: Handle the error case as described above.
2424- let qty = item_quantity.parse::<i32>();
2424+ let qty = item_quantity.parse::<i32>()?;
25252626 Ok(qty * cost_per_item + processing_fee)
2727}
+4-2
exercises/13_error_handling/errors3.rs
···15151616// TODO: Fix the compiler error by changing the signature and body of the
1717// `main` function.
1818-fn main() {
1818+fn main() -> Result<(), ParseIntError> {
1919 let mut tokens = 100;
2020 let pretend_user_input = "8";
2121···2727 } else {
2828 tokens -= cost;
2929 println!("You now have {tokens} tokens.");
3030- }
3030+ };
3131+3232+ Ok(())
3133}
+7-1
exercises/13_error_handling/errors4.rs
···11+use std::cmp::Ordering;
22+13#[derive(PartialEq, Debug)]
24enum CreationError {
35 Negative,
···1012impl PositiveNonzeroInteger {
1113 fn new(value: i64) -> Result<Self, CreationError> {
1214 // TODO: This function shouldn't always return an `Ok`.
1313- Ok(Self(value as u64))
1515+ match value.cmp(&0) {
1616+ Ordering::Greater => Ok(Self(value as u64)),
1717+ Ordering::Equal => Err(CreationError::Zero),
1818+ Ordering::Less => Err(CreationError::Negative),
1919+ }
1420 }
1521}
1622
+1-1
exercises/13_error_handling/errors5.rs
···48484949// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
5050// use to describe both errors? Is there a trait which both errors implement?
5151-fn main() {
5151+fn main() -> Result<(), Box<dyn Error>> {
5252 let pretend_user_input = "42";
5353 let x: i64 = pretend_user_input.parse()?;
5454 println!("output={:?}", PositiveNonzeroInteger::new(x)?);
+4-2
exercises/13_error_handling/errors6.rs
···2525 }
26262727 // TODO: Add another error conversion function here.
2828- // fn from_parse_int(???) -> Self { ??? }
2828+ fn from_parse_int(err: ParseIntError) -> Self {
2929+ Self::ParseInt(err)
3030+ }
2931}
30323133#[derive(PartialEq, Debug)]
···4345 fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
4446 // TODO: change this to return an appropriate error instead of panicking
4547 // when `parse()` returns an error.
4646- let x: i64 = s.parse().unwrap();
4848+ let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
4749 Self::new(x).map_err(ParsePosNonzeroError::from_creation)
4850 }
4951}
+35-2
solutions/13_error_handling/errors1.rs
···11+fn generate_nametag_text(name: String) -> Result<String, String> {
22+ // ^^^^^^ ^^^^^^
33+ if name.is_empty() {
44+ // `Err(String)` instead of `None`.
55+ Err("Empty names aren't allowed".to_string())
66+ } else {
77+ // `Ok` instead of `Some`.
88+ Ok(format!("Hi! My name is {name}"))
99+ }
1010+}
1111+112fn main() {
22- // DON'T EDIT THIS SOLUTION FILE!
33- // It will be automatically filled after you finish the exercise.
1313+ // You can optionally experiment here.
1414+}
1515+1616+#[cfg(test)]
1717+mod tests {
1818+ use super::*;
1919+2020+ #[test]
2121+ fn generates_nametag_text_for_a_nonempty_name() {
2222+ assert_eq!(
2323+ generate_nametag_text("Beyoncé".to_string()).as_deref(),
2424+ Ok("Hi! My name is Beyoncé"),
2525+ );
2626+ }
2727+2828+ #[test]
2929+ fn explains_why_generating_nametag_text_fails() {
3030+ assert_eq!(
3131+ generate_nametag_text(String::new())
3232+ .as_ref()
3333+ .map_err(|e| e.as_str()),
3434+ Err("Empty names aren't allowed"),
3535+ );
3636+ }
437}
+56-2
solutions/13_error_handling/errors2.rs
···11+// Say we're writing a game where you can buy items with tokens. All items cost
22+// 5 tokens, and whenever you purchase items there is a processing fee of 1
33+// token. A player of the game will type in how many items they want to buy, and
44+// the `total_cost` function will calculate the total cost of the items. Since
55+// the player typed in the quantity, we get it as a string. They might have
66+// typed anything, not just numbers!
77+//
88+// Right now, this function isn't handling the error case at all. What we want
99+// to do is: If we call the `total_cost` function on a string that is not a
1010+// number, that function will return a `ParseIntError`. In that case, we want to
1111+// immediately return that error from our function and not try to multiply and
1212+// add.
1313+//
1414+// There are at least two ways to implement this that are both correct. But one
1515+// is a lot shorter!
1616+1717+use std::num::ParseIntError;
1818+1919+#[allow(unused_variables)]
2020+fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
2121+ let processing_fee = 1;
2222+ let cost_per_item = 5;
2323+2424+ // Added `?` to propagate the error.
2525+ let qty = item_quantity.parse::<i32>()?;
2626+ // ^ added
2727+2828+ // Equivalent to this verbose version:
2929+ let qty = match item_quantity.parse::<i32>() {
3030+ Ok(v) => v,
3131+ Err(e) => return Err(e),
3232+ };
3333+3434+ Ok(qty * cost_per_item + processing_fee)
3535+}
3636+137fn main() {
22- // DON'T EDIT THIS SOLUTION FILE!
33- // It will be automatically filled after you finish the exercise.
3838+ // You can optionally experiment here.
3939+}
4040+4141+#[cfg(test)]
4242+mod tests {
4343+ use super::*;
4444+ use std::num::IntErrorKind;
4545+4646+ #[test]
4747+ fn item_quantity_is_a_valid_number() {
4848+ assert_eq!(total_cost("34"), Ok(171));
4949+ }
5050+5151+ #[test]
5252+ fn item_quantity_is_an_invalid_number() {
5353+ assert_eq!(
5454+ total_cost("beep boop").unwrap_err().kind(),
5555+ &IntErrorKind::InvalidDigit,
5656+ );
5757+ }
458}
+31-3
solutions/13_error_handling/errors3.rs
···11-fn main() {
22- // DON'T EDIT THIS SOLUTION FILE!
33- // It will be automatically filled after you finish the exercise.
11+// This is a program that is trying to use a completed version of the
22+// `total_cost` function from the previous exercise. It's not working though!
33+// Why not? What should we do to fix it?
44+55+use std::num::ParseIntError;
66+77+// Don't change this function.
88+fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
99+ let processing_fee = 1;
1010+ let cost_per_item = 5;
1111+ let qty = item_quantity.parse::<i32>()?;
1212+1313+ Ok(qty * cost_per_item + processing_fee)
1414+}
1515+1616+fn main() -> Result<(), ParseIntError> {
1717+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ added
1818+ let mut tokens = 100;
1919+ let pretend_user_input = "8";
2020+2121+ let cost = total_cost(pretend_user_input)?;
2222+2323+ if cost > tokens {
2424+ println!("You can't afford that many!");
2525+ } else {
2626+ tokens -= cost;
2727+ println!("You now have {tokens} tokens.");
2828+ }
2929+3030+ // Added this line to return the `Ok` variant of the expected `Result`.
3131+ Ok(())
432}
+40-2
solutions/13_error_handling/errors4.rs
···11+use std::cmp::Ordering;
22+33+#[derive(PartialEq, Debug)]
44+enum CreationError {
55+ Negative,
66+ Zero,
77+}
88+99+#[derive(PartialEq, Debug)]
1010+struct PositiveNonzeroInteger(u64);
1111+1212+impl PositiveNonzeroInteger {
1313+ fn new(value: i64) -> Result<Self, CreationError> {
1414+ match value.cmp(&0) {
1515+ Ordering::Less => Err(CreationError::Negative),
1616+ Ordering::Equal => Err(CreationError::Zero),
1717+ Ordering::Greater => Ok(Self(value as u64)),
1818+ }
1919+ }
2020+}
2121+122fn main() {
22- // DON'T EDIT THIS SOLUTION FILE!
33- // It will be automatically filled after you finish the exercise.
2323+ // You can optionally experiment here.
2424+}
2525+2626+#[cfg(test)]
2727+mod tests {
2828+ use super::*;
2929+3030+ #[test]
3131+ fn test_creation() {
3232+ assert_eq!(
3333+ PositiveNonzeroInteger::new(10),
3434+ Ok(PositiveNonzeroInteger(10)),
3535+ );
3636+ assert_eq!(
3737+ PositiveNonzeroInteger::new(-10),
3838+ Err(CreationError::Negative),
3939+ );
4040+ assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero));
4141+ }
442}
+53-3
solutions/13_error_handling/errors5.rs
···11-fn main() {
22- // DON'T EDIT THIS SOLUTION FILE!
33- // It will be automatically filled after you finish the exercise.
11+// This exercise is an altered version of the `errors4` exercise. It uses some
22+// concepts that we won't get to until later in the course, like `Box` and the
33+// `From` trait. It's not important to understand them in detail right now, but
44+// you can read ahead if you like. For now, think of the `Box<dyn ???>` type as
55+// an "I want anything that does ???" type.
66+//
77+// In short, this particular use case for boxes is for when you want to own a
88+// value and you care only that it is a type which implements a particular
99+// trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where
1010+// `Trait` is the trait the compiler looks for on any value used in that
1111+// context. For this exercise, that context is the potential errors which
1212+// can be returned in a `Result`.
1313+1414+use std::error::Error;
1515+use std::fmt;
1616+1717+#[derive(PartialEq, Debug)]
1818+enum CreationError {
1919+ Negative,
2020+ Zero,
2121+}
2222+2323+// This is required so that `CreationError` can implement `Error`.
2424+impl fmt::Display for CreationError {
2525+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2626+ let description = match *self {
2727+ CreationError::Negative => "number is negative",
2828+ CreationError::Zero => "number is zero",
2929+ };
3030+ f.write_str(description)
3131+ }
3232+}
3333+3434+impl Error for CreationError {}
3535+3636+#[derive(PartialEq, Debug)]
3737+struct PositiveNonzeroInteger(u64);
3838+3939+impl PositiveNonzeroInteger {
4040+ fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
4141+ match value {
4242+ x if x < 0 => Err(CreationError::Negative),
4343+ 0 => Err(CreationError::Zero),
4444+ x => Ok(PositiveNonzeroInteger(x as u64)),
4545+ }
4646+ }
4747+}
4848+4949+fn main() -> Result<(), Box<dyn Error>> {
5050+ let pretend_user_input = "42";
5151+ let x: i64 = pretend_user_input.parse()?;
5252+ println!("output={:?}", PositiveNonzeroInteger::new(x)?);
5353+ Ok(())
454}
+89-2
solutions/13_error_handling/errors6.rs
···11+// Using catch-all error types like `Box<dyn Error>` isn't recommended for
22+// library code where callers might want to make decisions based on the error
33+// content instead of printing it out or propagating it further. Here, we define
44+// a custom error type to make it possible for callers to decide what to do next
55+// when our function returns an error.
66+77+use std::num::ParseIntError;
88+99+#[derive(PartialEq, Debug)]
1010+enum CreationError {
1111+ Negative,
1212+ Zero,
1313+}
1414+1515+// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
1616+#[derive(PartialEq, Debug)]
1717+enum ParsePosNonzeroError {
1818+ Creation(CreationError),
1919+ ParseInt(ParseIntError),
2020+}
2121+2222+impl ParsePosNonzeroError {
2323+ fn from_creation(err: CreationError) -> Self {
2424+ Self::Creation(err)
2525+ }
2626+2727+ fn from_parse_int(err: ParseIntError) -> Self {
2828+ Self::ParseInt(err)
2929+ }
3030+}
3131+3232+#[derive(PartialEq, Debug)]
3333+struct PositiveNonzeroInteger(u64);
3434+3535+impl PositiveNonzeroInteger {
3636+ fn new(value: i64) -> Result<Self, CreationError> {
3737+ match value {
3838+ x if x < 0 => Err(CreationError::Negative),
3939+ 0 => Err(CreationError::Zero),
4040+ x => Ok(Self(x as u64)),
4141+ }
4242+ }
4343+4444+ fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
4545+ // Return an appropriate error instead of panicking when `parse()`
4646+ // returns an error.
4747+ let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
4848+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4949+ Self::new(x).map_err(ParsePosNonzeroError::from_creation)
5050+ }
5151+}
5252+153fn main() {
22- // DON'T EDIT THIS SOLUTION FILE!
33- // It will be automatically filled after you finish the exercise.
5454+ // You can optionally experiment here.
5555+}
5656+5757+#[cfg(test)]
5858+mod test {
5959+ use super::*;
6060+6161+ #[test]
6262+ fn test_parse_error() {
6363+ assert!(matches!(
6464+ PositiveNonzeroInteger::parse("not a number"),
6565+ Err(ParsePosNonzeroError::ParseInt(_)),
6666+ ));
6767+ }
6868+6969+ #[test]
7070+ fn test_negative() {
7171+ assert_eq!(
7272+ PositiveNonzeroInteger::parse("-555"),
7373+ Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
7474+ );
7575+ }
7676+7777+ #[test]
7878+ fn test_zero() {
7979+ assert_eq!(
8080+ PositiveNonzeroInteger::parse("0"),
8181+ Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
8282+ );
8383+ }
8484+8585+ #[test]
8686+ fn test_positive() {
8787+ let x = PositiveNonzeroInteger::new(42).unwrap();
8888+ assert_eq!(x.0, 42);
8989+ assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
9090+ }
491}