+8
-2
.rustlings-state.txt
+8
-2
.rustlings-state.txt
+3
-3
exercises/13_error_handling/errors1.rs
+3
-3
exercises/13_error_handling/errors1.rs
···
4
4
// construct to `Option` that can be used to express error conditions. Change
5
5
// the function signature and body to return `Result<String, String>` instead
6
6
// of `Option<String>`.
7
-
fn generate_nametag_text(name: String) -> Option<String> {
7
+
fn generate_nametag_text(name: String) -> Result<String, String> {
8
8
if name.is_empty() {
9
9
// Empty names aren't allowed
10
-
None
10
+
Err(String::from("Empty names aren't allowed"))
11
11
} else {
12
-
Some(format!("Hi! My name is {name}"))
12
+
Ok(format!("Hi! My name is {name}"))
13
13
}
14
14
}
15
15
+1
-1
exercises/13_error_handling/errors2.rs
+1
-1
exercises/13_error_handling/errors2.rs
+4
-2
exercises/13_error_handling/errors3.rs
+4
-2
exercises/13_error_handling/errors3.rs
···
15
15
16
16
// TODO: Fix the compiler error by changing the signature and body of the
17
17
// `main` function.
18
-
fn main() {
18
+
fn main() -> Result<(), ParseIntError> {
19
19
let mut tokens = 100;
20
20
let pretend_user_input = "8";
21
21
···
27
27
} else {
28
28
tokens -= cost;
29
29
println!("You now have {tokens} tokens.");
30
-
}
30
+
};
31
+
32
+
Ok(())
31
33
}
+7
-1
exercises/13_error_handling/errors4.rs
+7
-1
exercises/13_error_handling/errors4.rs
···
1
+
use std::cmp::Ordering;
2
+
1
3
#[derive(PartialEq, Debug)]
2
4
enum CreationError {
3
5
Negative,
···
10
12
impl PositiveNonzeroInteger {
11
13
fn new(value: i64) -> Result<Self, CreationError> {
12
14
// TODO: This function shouldn't always return an `Ok`.
13
-
Ok(Self(value as u64))
15
+
match value.cmp(&0) {
16
+
Ordering::Greater => Ok(Self(value as u64)),
17
+
Ordering::Equal => Err(CreationError::Zero),
18
+
Ordering::Less => Err(CreationError::Negative),
19
+
}
14
20
}
15
21
}
16
22
+1
-1
exercises/13_error_handling/errors5.rs
+1
-1
exercises/13_error_handling/errors5.rs
···
48
48
49
49
// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
50
50
// use to describe both errors? Is there a trait which both errors implement?
51
-
fn main() {
51
+
fn main() -> Result<(), Box<dyn Error>> {
52
52
let pretend_user_input = "42";
53
53
let x: i64 = pretend_user_input.parse()?;
54
54
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
+4
-2
exercises/13_error_handling/errors6.rs
+4
-2
exercises/13_error_handling/errors6.rs
···
25
25
}
26
26
27
27
// TODO: Add another error conversion function here.
28
-
// fn from_parse_int(???) -> Self { ??? }
28
+
fn from_parse_int(err: ParseIntError) -> Self {
29
+
Self::ParseInt(err)
30
+
}
29
31
}
30
32
31
33
#[derive(PartialEq, Debug)]
···
43
45
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
44
46
// TODO: change this to return an appropriate error instead of panicking
45
47
// when `parse()` returns an error.
46
-
let x: i64 = s.parse().unwrap();
48
+
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
47
49
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
48
50
}
49
51
}
+35
-2
solutions/13_error_handling/errors1.rs
+35
-2
solutions/13_error_handling/errors1.rs
···
1
+
fn generate_nametag_text(name: String) -> Result<String, String> {
2
+
// ^^^^^^ ^^^^^^
3
+
if name.is_empty() {
4
+
// `Err(String)` instead of `None`.
5
+
Err("Empty names aren't allowed".to_string())
6
+
} else {
7
+
// `Ok` instead of `Some`.
8
+
Ok(format!("Hi! My name is {name}"))
9
+
}
10
+
}
11
+
1
12
fn main() {
2
-
// DON'T EDIT THIS SOLUTION FILE!
3
-
// It will be automatically filled after you finish the exercise.
13
+
// You can optionally experiment here.
14
+
}
15
+
16
+
#[cfg(test)]
17
+
mod tests {
18
+
use super::*;
19
+
20
+
#[test]
21
+
fn generates_nametag_text_for_a_nonempty_name() {
22
+
assert_eq!(
23
+
generate_nametag_text("Beyoncé".to_string()).as_deref(),
24
+
Ok("Hi! My name is Beyoncé"),
25
+
);
26
+
}
27
+
28
+
#[test]
29
+
fn explains_why_generating_nametag_text_fails() {
30
+
assert_eq!(
31
+
generate_nametag_text(String::new())
32
+
.as_ref()
33
+
.map_err(|e| e.as_str()),
34
+
Err("Empty names aren't allowed"),
35
+
);
36
+
}
4
37
}
+56
-2
solutions/13_error_handling/errors2.rs
+56
-2
solutions/13_error_handling/errors2.rs
···
1
+
// Say we're writing a game where you can buy items with tokens. All items cost
2
+
// 5 tokens, and whenever you purchase items there is a processing fee of 1
3
+
// token. A player of the game will type in how many items they want to buy, and
4
+
// the `total_cost` function will calculate the total cost of the items. Since
5
+
// the player typed in the quantity, we get it as a string. They might have
6
+
// typed anything, not just numbers!
7
+
//
8
+
// Right now, this function isn't handling the error case at all. What we want
9
+
// to do is: If we call the `total_cost` function on a string that is not a
10
+
// number, that function will return a `ParseIntError`. In that case, we want to
11
+
// immediately return that error from our function and not try to multiply and
12
+
// add.
13
+
//
14
+
// There are at least two ways to implement this that are both correct. But one
15
+
// is a lot shorter!
16
+
17
+
use std::num::ParseIntError;
18
+
19
+
#[allow(unused_variables)]
20
+
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
21
+
let processing_fee = 1;
22
+
let cost_per_item = 5;
23
+
24
+
// Added `?` to propagate the error.
25
+
let qty = item_quantity.parse::<i32>()?;
26
+
// ^ added
27
+
28
+
// Equivalent to this verbose version:
29
+
let qty = match item_quantity.parse::<i32>() {
30
+
Ok(v) => v,
31
+
Err(e) => return Err(e),
32
+
};
33
+
34
+
Ok(qty * cost_per_item + processing_fee)
35
+
}
36
+
1
37
fn main() {
2
-
// DON'T EDIT THIS SOLUTION FILE!
3
-
// It will be automatically filled after you finish the exercise.
38
+
// You can optionally experiment here.
39
+
}
40
+
41
+
#[cfg(test)]
42
+
mod tests {
43
+
use super::*;
44
+
use std::num::IntErrorKind;
45
+
46
+
#[test]
47
+
fn item_quantity_is_a_valid_number() {
48
+
assert_eq!(total_cost("34"), Ok(171));
49
+
}
50
+
51
+
#[test]
52
+
fn item_quantity_is_an_invalid_number() {
53
+
assert_eq!(
54
+
total_cost("beep boop").unwrap_err().kind(),
55
+
&IntErrorKind::InvalidDigit,
56
+
);
57
+
}
4
58
}
+31
-3
solutions/13_error_handling/errors3.rs
+31
-3
solutions/13_error_handling/errors3.rs
···
1
-
fn main() {
2
-
// DON'T EDIT THIS SOLUTION FILE!
3
-
// It will be automatically filled after you finish the exercise.
1
+
// This is a program that is trying to use a completed version of the
2
+
// `total_cost` function from the previous exercise. It's not working though!
3
+
// Why not? What should we do to fix it?
4
+
5
+
use std::num::ParseIntError;
6
+
7
+
// Don't change this function.
8
+
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
9
+
let processing_fee = 1;
10
+
let cost_per_item = 5;
11
+
let qty = item_quantity.parse::<i32>()?;
12
+
13
+
Ok(qty * cost_per_item + processing_fee)
14
+
}
15
+
16
+
fn main() -> Result<(), ParseIntError> {
17
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ added
18
+
let mut tokens = 100;
19
+
let pretend_user_input = "8";
20
+
21
+
let cost = total_cost(pretend_user_input)?;
22
+
23
+
if cost > tokens {
24
+
println!("You can't afford that many!");
25
+
} else {
26
+
tokens -= cost;
27
+
println!("You now have {tokens} tokens.");
28
+
}
29
+
30
+
// Added this line to return the `Ok` variant of the expected `Result`.
31
+
Ok(())
4
32
}
+40
-2
solutions/13_error_handling/errors4.rs
+40
-2
solutions/13_error_handling/errors4.rs
···
1
+
use std::cmp::Ordering;
2
+
3
+
#[derive(PartialEq, Debug)]
4
+
enum CreationError {
5
+
Negative,
6
+
Zero,
7
+
}
8
+
9
+
#[derive(PartialEq, Debug)]
10
+
struct PositiveNonzeroInteger(u64);
11
+
12
+
impl PositiveNonzeroInteger {
13
+
fn new(value: i64) -> Result<Self, CreationError> {
14
+
match value.cmp(&0) {
15
+
Ordering::Less => Err(CreationError::Negative),
16
+
Ordering::Equal => Err(CreationError::Zero),
17
+
Ordering::Greater => Ok(Self(value as u64)),
18
+
}
19
+
}
20
+
}
21
+
1
22
fn main() {
2
-
// DON'T EDIT THIS SOLUTION FILE!
3
-
// It will be automatically filled after you finish the exercise.
23
+
// You can optionally experiment here.
24
+
}
25
+
26
+
#[cfg(test)]
27
+
mod tests {
28
+
use super::*;
29
+
30
+
#[test]
31
+
fn test_creation() {
32
+
assert_eq!(
33
+
PositiveNonzeroInteger::new(10),
34
+
Ok(PositiveNonzeroInteger(10)),
35
+
);
36
+
assert_eq!(
37
+
PositiveNonzeroInteger::new(-10),
38
+
Err(CreationError::Negative),
39
+
);
40
+
assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero));
41
+
}
4
42
}
+53
-3
solutions/13_error_handling/errors5.rs
+53
-3
solutions/13_error_handling/errors5.rs
···
1
-
fn main() {
2
-
// DON'T EDIT THIS SOLUTION FILE!
3
-
// It will be automatically filled after you finish the exercise.
1
+
// This exercise is an altered version of the `errors4` exercise. It uses some
2
+
// concepts that we won't get to until later in the course, like `Box` and the
3
+
// `From` trait. It's not important to understand them in detail right now, but
4
+
// you can read ahead if you like. For now, think of the `Box<dyn ???>` type as
5
+
// an "I want anything that does ???" type.
6
+
//
7
+
// In short, this particular use case for boxes is for when you want to own a
8
+
// value and you care only that it is a type which implements a particular
9
+
// trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where
10
+
// `Trait` is the trait the compiler looks for on any value used in that
11
+
// context. For this exercise, that context is the potential errors which
12
+
// can be returned in a `Result`.
13
+
14
+
use std::error::Error;
15
+
use std::fmt;
16
+
17
+
#[derive(PartialEq, Debug)]
18
+
enum CreationError {
19
+
Negative,
20
+
Zero,
21
+
}
22
+
23
+
// This is required so that `CreationError` can implement `Error`.
24
+
impl fmt::Display for CreationError {
25
+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26
+
let description = match *self {
27
+
CreationError::Negative => "number is negative",
28
+
CreationError::Zero => "number is zero",
29
+
};
30
+
f.write_str(description)
31
+
}
32
+
}
33
+
34
+
impl Error for CreationError {}
35
+
36
+
#[derive(PartialEq, Debug)]
37
+
struct PositiveNonzeroInteger(u64);
38
+
39
+
impl PositiveNonzeroInteger {
40
+
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
41
+
match value {
42
+
x if x < 0 => Err(CreationError::Negative),
43
+
0 => Err(CreationError::Zero),
44
+
x => Ok(PositiveNonzeroInteger(x as u64)),
45
+
}
46
+
}
47
+
}
48
+
49
+
fn main() -> Result<(), Box<dyn Error>> {
50
+
let pretend_user_input = "42";
51
+
let x: i64 = pretend_user_input.parse()?;
52
+
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
53
+
Ok(())
4
54
}
+89
-2
solutions/13_error_handling/errors6.rs
+89
-2
solutions/13_error_handling/errors6.rs
···
1
+
// Using catch-all error types like `Box<dyn Error>` isn't recommended for
2
+
// library code where callers might want to make decisions based on the error
3
+
// content instead of printing it out or propagating it further. Here, we define
4
+
// a custom error type to make it possible for callers to decide what to do next
5
+
// when our function returns an error.
6
+
7
+
use std::num::ParseIntError;
8
+
9
+
#[derive(PartialEq, Debug)]
10
+
enum CreationError {
11
+
Negative,
12
+
Zero,
13
+
}
14
+
15
+
// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
16
+
#[derive(PartialEq, Debug)]
17
+
enum ParsePosNonzeroError {
18
+
Creation(CreationError),
19
+
ParseInt(ParseIntError),
20
+
}
21
+
22
+
impl ParsePosNonzeroError {
23
+
fn from_creation(err: CreationError) -> Self {
24
+
Self::Creation(err)
25
+
}
26
+
27
+
fn from_parse_int(err: ParseIntError) -> Self {
28
+
Self::ParseInt(err)
29
+
}
30
+
}
31
+
32
+
#[derive(PartialEq, Debug)]
33
+
struct PositiveNonzeroInteger(u64);
34
+
35
+
impl PositiveNonzeroInteger {
36
+
fn new(value: i64) -> Result<Self, CreationError> {
37
+
match value {
38
+
x if x < 0 => Err(CreationError::Negative),
39
+
0 => Err(CreationError::Zero),
40
+
x => Ok(Self(x as u64)),
41
+
}
42
+
}
43
+
44
+
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
45
+
// Return an appropriate error instead of panicking when `parse()`
46
+
// returns an error.
47
+
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
48
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
49
+
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
50
+
}
51
+
}
52
+
1
53
fn main() {
2
-
// DON'T EDIT THIS SOLUTION FILE!
3
-
// It will be automatically filled after you finish the exercise.
54
+
// You can optionally experiment here.
55
+
}
56
+
57
+
#[cfg(test)]
58
+
mod test {
59
+
use super::*;
60
+
61
+
#[test]
62
+
fn test_parse_error() {
63
+
assert!(matches!(
64
+
PositiveNonzeroInteger::parse("not a number"),
65
+
Err(ParsePosNonzeroError::ParseInt(_)),
66
+
));
67
+
}
68
+
69
+
#[test]
70
+
fn test_negative() {
71
+
assert_eq!(
72
+
PositiveNonzeroInteger::parse("-555"),
73
+
Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
74
+
);
75
+
}
76
+
77
+
#[test]
78
+
fn test_zero() {
79
+
assert_eq!(
80
+
PositiveNonzeroInteger::parse("0"),
81
+
Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
82
+
);
83
+
}
84
+
85
+
#[test]
86
+
fn test_positive() {
87
+
let x = PositiveNonzeroInteger::new(42).unwrap();
88
+
assert_eq!(x.0, 42);
89
+
assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
90
+
}
4
91
}