finish errors
This commit is contained in:
parent
63d7568a22
commit
7e6588f6f0
13 changed files with 344 additions and 32 deletions
|
@ -1,6 +1,6 @@
|
||||||
DON'T EDIT THIS FILE!
|
DON'T EDIT THIS FILE!
|
||||||
|
|
||||||
errors1
|
generics1
|
||||||
|
|
||||||
intro1
|
intro1
|
||||||
intro2
|
intro2
|
||||||
|
@ -51,4 +51,10 @@ hashmaps3
|
||||||
quiz2
|
quiz2
|
||||||
options1
|
options1
|
||||||
options2
|
options2
|
||||||
options3
|
options3
|
||||||
|
errors1
|
||||||
|
errors2
|
||||||
|
errors3
|
||||||
|
errors4
|
||||||
|
errors5
|
||||||
|
errors6
|
|
@ -4,12 +4,12 @@
|
||||||
// construct to `Option` that can be used to express error conditions. Change
|
// construct to `Option` that can be used to express error conditions. Change
|
||||||
// the function signature and body to return `Result<String, String>` instead
|
// the function signature and body to return `Result<String, String>` instead
|
||||||
// of `Option<String>`.
|
// of `Option<String>`.
|
||||||
fn generate_nametag_text(name: String) -> Option<String> {
|
fn generate_nametag_text(name: String) -> Result<String, String> {
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
// Empty names aren't allowed
|
// Empty names aren't allowed
|
||||||
None
|
Err("Empty names aren't allowed".to_string())
|
||||||
} else {
|
} else {
|
||||||
Some(format!("Hi! My name is {name}"))
|
Ok(format!("Hi! My name is {name}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,16 @@ fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||||
// TODO: Handle the error case as described above.
|
// TODO: Handle the error case as described above.
|
||||||
let qty = item_quantity.parse::<i32>();
|
let qty = item_quantity.parse::<i32>();
|
||||||
|
|
||||||
Ok(qty * cost_per_item + processing_fee)
|
match qty {
|
||||||
|
Ok(value) => {
|
||||||
|
Ok(value * cost_per_item + processing_fee)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -15,7 +15,7 @@ fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||||
|
|
||||||
// TODO: Fix the compiler error by changing the signature and body of the
|
// TODO: Fix the compiler error by changing the signature and body of the
|
||||||
// `main` function.
|
// `main` function.
|
||||||
fn main() {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut tokens = 100;
|
let mut tokens = 100;
|
||||||
let pretend_user_input = "8";
|
let pretend_user_input = "8";
|
||||||
|
|
||||||
|
@ -28,4 +28,5 @@ fn main() {
|
||||||
tokens -= cost;
|
tokens -= cost;
|
||||||
println!("You now have {tokens} tokens.");
|
println!("You now have {tokens} tokens.");
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,12 @@ struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
impl PositiveNonzeroInteger {
|
impl PositiveNonzeroInteger {
|
||||||
fn new(value: i64) -> Result<Self, CreationError> {
|
fn new(value: i64) -> Result<Self, CreationError> {
|
||||||
// TODO: This function shouldn't always return an `Ok`.
|
if value < 0 {
|
||||||
|
return Err(CreationError::Negative);
|
||||||
|
}
|
||||||
|
if value == 0 {
|
||||||
|
return Err(CreationError::Zero);
|
||||||
|
}
|
||||||
Ok(Self(value as u64))
|
Ok(Self(value as u64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl PositiveNonzeroInteger {
|
||||||
|
|
||||||
// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
|
// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
|
||||||
// use to describe both errors? Is there a trait which both errors implement?
|
// use to describe both errors? Is there a trait which both errors implement?
|
||||||
fn main() {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let pretend_user_input = "42";
|
let pretend_user_input = "42";
|
||||||
let x: i64 = pretend_user_input.parse()?;
|
let x: i64 = pretend_user_input.parse()?;
|
||||||
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
|
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
|
||||||
|
|
|
@ -24,8 +24,9 @@ impl ParsePosNonzeroError {
|
||||||
Self::Creation(err)
|
Self::Creation(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add another error conversion function here.
|
fn from_parse_int(err: ParseIntError) -> Self {
|
||||||
// fn from_parse_int(???) -> Self { ??? }
|
Self::ParseInt(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
|
@ -41,9 +42,9 @@ impl PositiveNonzeroInteger {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
||||||
// TODO: change this to return an appropriate error instead of panicking
|
let x: i64 = s.parse().map_err(|e| {
|
||||||
// when `parse()` returns an error.
|
ParsePosNonzeroError::from_parse_int(e)
|
||||||
let x: i64 = s.parse().unwrap();
|
})?;
|
||||||
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,37 @@
|
||||||
fn main() {
|
fn generate_nametag_text(name: String) -> Result<String, String> {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// ^^^^^^ ^^^^^^
|
||||||
// It will be automatically filled after you finish the exercise.
|
if name.is_empty() {
|
||||||
|
// `Err(String)` instead of `None`.
|
||||||
|
Err("Empty names aren't allowed".to_string())
|
||||||
|
} else {
|
||||||
|
// `Ok` instead of `Some`.
|
||||||
|
Ok(format!("Hi! My name is {name}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generates_nametag_text_for_a_nonempty_name() {
|
||||||
|
assert_eq!(
|
||||||
|
generate_nametag_text("Beyoncé".to_string()).as_deref(),
|
||||||
|
Ok("Hi! My name is Beyoncé"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explains_why_generating_nametag_text_fails() {
|
||||||
|
assert_eq!(
|
||||||
|
generate_nametag_text(String::new())
|
||||||
|
.as_ref()
|
||||||
|
.map_err(|e| e.as_str()),
|
||||||
|
Err("Empty names aren't allowed"),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,58 @@
|
||||||
fn main() {
|
// Say we're writing a game where you can buy items with tokens. All items cost
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// 5 tokens, and whenever you purchase items there is a processing fee of 1
|
||||||
// It will be automatically filled after you finish the exercise.
|
// 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, we get it as a string. They might have
|
||||||
|
// typed anything, not just numbers!
|
||||||
|
//
|
||||||
|
// Right now, this function isn't handling the error case at all. 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`. 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!
|
||||||
|
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||||
|
let processing_fee = 1;
|
||||||
|
let cost_per_item = 5;
|
||||||
|
|
||||||
|
// Added `?` to propagate the error.
|
||||||
|
let qty = item_quantity.parse::<i32>()?;
|
||||||
|
// ^ added
|
||||||
|
|
||||||
|
// Equivalent to this verbose version:
|
||||||
|
let qty = match item_quantity.parse::<i32>() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(qty * cost_per_item + processing_fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::num::IntErrorKind;
|
||||||
|
|
||||||
|
#[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().kind(),
|
||||||
|
&IntErrorKind::InvalidDigit,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,32 @@
|
||||||
fn main() {
|
// This is a program that is trying to use a completed version of the
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// `total_cost` function from the previous exercise. It's not working though!
|
||||||
// It will be automatically filled after you finish the exercise.
|
// Why not? What should we do to fix it?
|
||||||
|
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
// Don't change this function.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), ParseIntError> {
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ added
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added this line to return the `Ok` variant of the expected `Result`.
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,42 @@
|
||||||
fn main() {
|
use std::cmp::Ordering;
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
|
||||||
// It will be automatically filled after you finish the exercise.
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum CreationError {
|
||||||
|
Negative,
|
||||||
|
Zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
|
impl PositiveNonzeroInteger {
|
||||||
|
fn new(value: i64) -> Result<Self, CreationError> {
|
||||||
|
match value.cmp(&0) {
|
||||||
|
Ordering::Less => Err(CreationError::Negative),
|
||||||
|
Ordering::Equal => Err(CreationError::Zero),
|
||||||
|
Ordering::Greater => Ok(Self(value as u64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_creation() {
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::new(10),
|
||||||
|
Ok(PositiveNonzeroInteger(10)),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::new(-10),
|
||||||
|
Err(CreationError::Negative),
|
||||||
|
);
|
||||||
|
assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,54 @@
|
||||||
fn main() {
|
// This exercise is an altered version of the `errors4` exercise. It uses some
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// concepts that we won't get to until later in the course, like `Box` and the
|
||||||
// It will be automatically filled after you finish the exercise.
|
// `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.
|
||||||
|
//
|
||||||
|
// 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`.
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum CreationError {
|
||||||
|
Negative,
|
||||||
|
Zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is required so that `CreationError` can implement `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 for CreationError {}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
|
impl PositiveNonzeroInteger {
|
||||||
|
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
||||||
|
match value {
|
||||||
|
x if x < 0 => Err(CreationError::Negative),
|
||||||
|
0 => Err(CreationError::Zero),
|
||||||
|
x => Ok(PositiveNonzeroInteger(x as u64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let pretend_user_input = "42";
|
||||||
|
let x: i64 = pretend_user_input.parse()?;
|
||||||
|
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,91 @@
|
||||||
fn main() {
|
// Using catch-all error types like `Box<dyn Error>` isn't recommended for
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// library code where callers might want to make decisions based on the error
|
||||||
// It will be automatically filled after you finish the exercise.
|
// 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.
|
||||||
|
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum CreationError {
|
||||||
|
Negative,
|
||||||
|
Zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum ParsePosNonzeroError {
|
||||||
|
Creation(CreationError),
|
||||||
|
ParseInt(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsePosNonzeroError {
|
||||||
|
fn from_creation(err: CreationError) -> Self {
|
||||||
|
Self::Creation(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_parse_int(err: ParseIntError) -> Self {
|
||||||
|
Self::ParseInt(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
|
impl PositiveNonzeroInteger {
|
||||||
|
fn new(value: i64) -> Result<Self, CreationError> {
|
||||||
|
match value {
|
||||||
|
x if x < 0 => Err(CreationError::Negative),
|
||||||
|
0 => Err(CreationError::Zero),
|
||||||
|
x => Ok(Self(x as u64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
||||||
|
// Return an appropriate error instead of panicking when `parse()`
|
||||||
|
// returns an error.
|
||||||
|
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_error() {
|
||||||
|
assert!(matches!(
|
||||||
|
PositiveNonzeroInteger::parse("not a number"),
|
||||||
|
Err(ParsePosNonzeroError::ParseInt(_)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_negative() {
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::parse("-555"),
|
||||||
|
Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero() {
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::parse("0"),
|
||||||
|
Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_positive() {
|
||||||
|
let x = PositiveNonzeroInteger::new(42).unwrap();
|
||||||
|
assert_eq!(x.0, 42);
|
||||||
|
assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue