mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-08-06 22:33:08 +02:00
Update Exercises Directory Names to Reflect Order
This commit is contained in:
12
exercises/13_error_handling/README.md
Normal file
12
exercises/13_error_handling/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Error handling
|
||||
|
||||
Most errors aren’t serious enough to require the program to stop entirely.
|
||||
Sometimes, when a function fails, it’s for a reason that you can easily interpret and respond to.
|
||||
For example, if you try to open a file and that operation fails because the file doesn’t exist, you might want to create the file instead of terminating the process.
|
||||
|
||||
## Further information
|
||||
|
||||
- [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)
|
||||
- [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html)
|
||||
- [Result](https://doc.rust-lang.org/rust-by-example/error/result.html)
|
||||
- [Boxing errors](https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/boxing_errors.html)
|
43
exercises/13_error_handling/errors1.rs
Normal file
43
exercises/13_error_handling/errors1.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// errors1.rs
|
||||
//
|
||||
// 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`. Thankfully, Rust has a similar
|
||||
// construct to `Option` that can be used to express error conditions. Let's use
|
||||
// it!
|
||||
//
|
||||
// Execute `rustlings hint errors1` or use the `hint` watch subcommand for a
|
||||
// hint.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
pub fn generate_nametag_text(name: String) -> Option<String> {
|
||||
if name.is_empty() {
|
||||
// Empty names aren't allowed.
|
||||
None
|
||||
} else {
|
||||
Some(format!("Hi! My name is {}", name))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn generates_nametag_text_for_a_nonempty_name() {
|
||||
assert_eq!(
|
||||
generate_nametag_text("Beyoncé".into()),
|
||||
Ok("Hi! My name is Beyoncé".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explains_why_generating_nametag_text_fails() {
|
||||
assert_eq!(
|
||||
generate_nametag_text("".into()),
|
||||
// Don't change this line
|
||||
Err("`name` was empty; it must be nonempty.".into())
|
||||
);
|
||||
}
|
||||
}
|
50
exercises/13_error_handling/errors2.rs
Normal file
50
exercises/13_error_handling/errors2.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// errors2.rs
|
||||
//
|
||||
// Say we're writing a game where you can buy items with tokens. All items cost
|
||||
// 5 tokens, and whenever you purchase items there is a processing fee of 1
|
||||
// token. A player of the game will type in how many items they want to buy, and
|
||||
// the `total_cost` function will calculate the total cost of the items. Since
|
||||
// the player typed in the quantity, though, we get it as a string-- and they
|
||||
// might have typed anything, not just numbers!
|
||||
//
|
||||
// Right now, this function isn't handling the error case at all (and isn't
|
||||
// handling the success case properly either). What we want to do is: if we call
|
||||
// the `total_cost` function on a string that is not a number, that function
|
||||
// will return a `ParseIntError`, and in that case, we want to immediately
|
||||
// return that error from our function and not try to multiply and add.
|
||||
//
|
||||
// There are at least two ways to implement this that are both correct-- but one
|
||||
// is a lot shorter!
|
||||
//
|
||||
// Execute `rustlings hint errors2` or use the `hint` watch subcommand for a
|
||||
// hint.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||
let processing_fee = 1;
|
||||
let cost_per_item = 5;
|
||||
let qty = item_quantity.parse::<i32>();
|
||||
|
||||
Ok(qty * cost_per_item + processing_fee)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn item_quantity_is_a_valid_number() {
|
||||
assert_eq!(total_cost("34"), Ok(171));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item_quantity_is_an_invalid_number() {
|
||||
assert_eq!(
|
||||
total_cost("beep boop").unwrap_err().to_string(),
|
||||
"invalid digit found in string"
|
||||
);
|
||||
}
|
||||
}
|
34
exercises/13_error_handling/errors3.rs
Normal file
34
exercises/13_error_handling/errors3.rs
Normal file
@ -0,0 +1,34 @@
|
||||
// errors3.rs
|
||||
//
|
||||
// This is a program that is trying to use a completed version of the
|
||||
// `total_cost` function from the previous exercise. It's not working though!
|
||||
// Why not? What should we do to fix it?
|
||||
//
|
||||
// Execute `rustlings hint errors3` or use the `hint` watch subcommand for a
|
||||
// hint.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
fn main() {
|
||||
let mut tokens = 100;
|
||||
let pretend_user_input = "8";
|
||||
|
||||
let cost = total_cost(pretend_user_input)?;
|
||||
|
||||
if cost > tokens {
|
||||
println!("You can't afford that many!");
|
||||
} else {
|
||||
tokens -= cost;
|
||||
println!("You now have {} tokens.", tokens);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||
let processing_fee = 1;
|
||||
let cost_per_item = 5;
|
||||
let qty = item_quantity.parse::<i32>()?;
|
||||
|
||||
Ok(qty * cost_per_item + processing_fee)
|
||||
}
|
32
exercises/13_error_handling/errors4.rs
Normal file
32
exercises/13_error_handling/errors4.rs
Normal file
@ -0,0 +1,32 @@
|
||||
// errors4.rs
|
||||
//
|
||||
// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a
|
||||
// hint.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct PositiveNonzeroInteger(u64);
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
Zero,
|
||||
}
|
||||
|
||||
impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
||||
// Hmm... Why is this always returning an Ok value?
|
||||
Ok(PositiveNonzeroInteger(value as u64))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_creation() {
|
||||
assert!(PositiveNonzeroInteger::new(10).is_ok());
|
||||
assert_eq!(
|
||||
Err(CreationError::Negative),
|
||||
PositiveNonzeroInteger::new(-10)
|
||||
);
|
||||
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
|
||||
}
|
71
exercises/13_error_handling/errors5.rs
Normal file
71
exercises/13_error_handling/errors5.rs
Normal file
@ -0,0 +1,71 @@
|
||||
// errors5.rs
|
||||
//
|
||||
// This program uses an altered version of the code from errors4.
|
||||
//
|
||||
// This exercise uses some concepts that we won't get to until later in the
|
||||
// course, like `Box` and the `From` trait. It's not important to understand
|
||||
// them in detail right now, but you can read ahead if you like. For now, think
|
||||
// of the `Box<dyn ???>` type as an "I want anything that does ???" type, which,
|
||||
// given Rust's usual standards for runtime safety, should strike you as
|
||||
// somewhat lenient!
|
||||
//
|
||||
// In short, this particular use case for boxes is for when you want to own a
|
||||
// value and you care only that it is a type which implements a particular
|
||||
// trait. To do so, The Box is declared as of type Box<dyn Trait> where Trait is
|
||||
// the trait the compiler looks for on any value used in that context. For this
|
||||
// exercise, that context is the potential errors which can be returned in a
|
||||
// Result.
|
||||
//
|
||||
// What can we use to describe both errors? In other words, is there a trait
|
||||
// which both errors implement?
|
||||
//
|
||||
// Execute `rustlings hint errors5` or use the `hint` watch subcommand for a
|
||||
// hint.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
// TODO: update the return type of `main()` to make this compile.
|
||||
fn main() -> Result<(), Box<dyn ???>> {
|
||||
let pretend_user_input = "42";
|
||||
let x: i64 = pretend_user_input.parse()?;
|
||||
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Don't change anything below this line.
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct PositiveNonzeroInteger(u64);
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
Zero,
|
||||
}
|
||||
|
||||
impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
||||
match value {
|
||||
x if x < 0 => Err(CreationError::Negative),
|
||||
x if x == 0 => Err(CreationError::Zero),
|
||||
x => Ok(PositiveNonzeroInteger(x as u64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is required so that `CreationError` can implement `error::Error`.
|
||||
impl fmt::Display for CreationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let description = match *self {
|
||||
CreationError::Negative => "number is negative",
|
||||
CreationError::Zero => "number is zero",
|
||||
};
|
||||
f.write_str(description)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for CreationError {}
|
94
exercises/13_error_handling/errors6.rs
Normal file
94
exercises/13_error_handling/errors6.rs
Normal file
@ -0,0 +1,94 @@
|
||||
// errors6.rs
|
||||
//
|
||||
// Using catch-all error types like `Box<dyn error::Error>` isn't recommended
|
||||
// for library code, where callers might want to make decisions based on the
|
||||
// error content, instead of printing it out or propagating it further. Here, we
|
||||
// define a custom error type to make it possible for callers to decide what to
|
||||
// do next when our function returns an error.
|
||||
//
|
||||
// Execute `rustlings hint errors6` or use the `hint` watch subcommand for a
|
||||
// hint.
|
||||
|
||||
// I AM NOT DONE
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
// This is a custom error type that we will be using in `parse_pos_nonzero()`.
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum ParsePosNonzeroError {
|
||||
Creation(CreationError),
|
||||
ParseInt(ParseIntError),
|
||||
}
|
||||
|
||||
impl ParsePosNonzeroError {
|
||||
fn from_creation(err: CreationError) -> ParsePosNonzeroError {
|
||||
ParsePosNonzeroError::Creation(err)
|
||||
}
|
||||
// TODO: add another error conversion function here.
|
||||
// fn from_parseint...
|
||||
}
|
||||
|
||||
fn parse_pos_nonzero(s: &str) -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> {
|
||||
// TODO: change this to return an appropriate error instead of panicking
|
||||
// when `parse()` returns an error.
|
||||
let x: i64 = s.parse().unwrap();
|
||||
PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||
}
|
||||
|
||||
// Don't change anything below this line.
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct PositiveNonzeroInteger(u64);
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
Zero,
|
||||
}
|
||||
|
||||
impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
||||
match value {
|
||||
x if x < 0 => Err(CreationError::Negative),
|
||||
x if x == 0 => Err(CreationError::Zero),
|
||||
x => Ok(PositiveNonzeroInteger(x as u64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_error() {
|
||||
// We can't construct a ParseIntError, so we have to pattern match.
|
||||
assert!(matches!(
|
||||
parse_pos_nonzero("not a number"),
|
||||
Err(ParsePosNonzeroError::ParseInt(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negative() {
|
||||
assert_eq!(
|
||||
parse_pos_nonzero("-555"),
|
||||
Err(ParsePosNonzeroError::Creation(CreationError::Negative))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero() {
|
||||
assert_eq!(
|
||||
parse_pos_nonzero("0"),
|
||||
Err(ParsePosNonzeroError::Creation(CreationError::Zero))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_positive() {
|
||||
let x = PositiveNonzeroInteger::new(42);
|
||||
assert!(x.is_ok());
|
||||
assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap()));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user