mirror of https://github.com/rust-lang/rustlings
Merge pull request #772 from tlyu/errors-rework
feature: improve error_handling exercisespull/779/head
commit
ec63cadadb
@ -1,5 +1,5 @@
|
|||||||
// result1.rs
|
// errors4.rs
|
||||||
// Make this test pass! Execute `rustlings hint result1` for hints :)
|
// Make this test pass! Execute `rustlings hint errors4` for hints :)
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
@ -0,0 +1,53 @@
|
|||||||
|
// errors5.rs
|
||||||
|
|
||||||
|
// This program uses a completed version of the code from errors4.
|
||||||
|
// It won't compile right now! Why?
|
||||||
|
// Execute `rustlings hint errors5` for hints!
|
||||||
|
|
||||||
|
// 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<(), ParseIntError> {
|
||||||
|
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 {}
|
@ -0,0 +1,95 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Make these tests pass! Execute `rustlings hint errors6` for hints :)
|
||||||
|
|
||||||
|
// 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 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()));
|
||||||
|
}
|
||||||
|
}
|
@ -1,117 +0,0 @@
|
|||||||
// errorsn.rs
|
|
||||||
// This is a bigger error exercise than the previous ones!
|
|
||||||
// You can do it! :)
|
|
||||||
//
|
|
||||||
// Edit the `read_and_validate` function ONLY. Don't create any Errors
|
|
||||||
// that do not already exist.
|
|
||||||
//
|
|
||||||
// So many things could go wrong!
|
|
||||||
//
|
|
||||||
// - Reading from stdin could produce an io::Error
|
|
||||||
// - Parsing the input could produce a num::ParseIntError
|
|
||||||
// - Validating the input could produce a CreationError (defined below)
|
|
||||||
//
|
|
||||||
// How can we lump these errors into one general error? That is, what
|
|
||||||
// type goes where the question marks are, and how do we return
|
|
||||||
// that type from the body of read_and_validate?
|
|
||||||
//
|
|
||||||
// Execute `rustlings hint errorsn` for hints :)
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
|
||||||
|
|
||||||
use std::error;
|
|
||||||
use std::fmt;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
// PositiveNonzeroInteger is a struct defined below the tests.
|
|
||||||
fn read_and_validate(b: &mut dyn io::BufRead) -> Result<PositiveNonzeroInteger, ???> {
|
|
||||||
let mut line = String::new();
|
|
||||||
b.read_line(&mut line);
|
|
||||||
let num: i64 = line.trim().parse();
|
|
||||||
let answer = PositiveNonzeroInteger::new(num);
|
|
||||||
answer
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Nothing below this needs to be modified
|
|
||||||
//
|
|
||||||
|
|
||||||
// This is a test helper function that turns a &str into a BufReader.
|
|
||||||
fn test_with_str(s: &str) -> Result<PositiveNonzeroInteger, Box<dyn error::Error>> {
|
|
||||||
let mut b = io::BufReader::new(s.as_bytes());
|
|
||||||
read_and_validate(&mut b)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_success() {
|
|
||||||
let x = test_with_str("42\n");
|
|
||||||
assert_eq!(PositiveNonzeroInteger(42), x.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_not_num() {
|
|
||||||
let x = test_with_str("eleven billion\n");
|
|
||||||
assert!(x.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_non_positive() {
|
|
||||||
let x = test_with_str("-40\n");
|
|
||||||
assert!(x.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ioerror() {
|
|
||||||
struct Broken;
|
|
||||||
impl io::Read for Broken {
|
|
||||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut b = io::BufReader::new(Broken);
|
|
||||||
assert!(read_and_validate(&mut b).is_err());
|
|
||||||
assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
struct PositiveNonzeroInteger(u64);
|
|
||||||
|
|
||||||
impl PositiveNonzeroInteger {
|
|
||||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
|
||||||
if value == 0 {
|
|
||||||
Err(CreationError::Zero)
|
|
||||||
} else if value < 0 {
|
|
||||||
Err(CreationError::Negative)
|
|
||||||
} else {
|
|
||||||
Ok(PositiveNonzeroInteger(value as u64))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_positive_nonzero_integer_creation() {
|
|
||||||
assert!(PositiveNonzeroInteger::new(10).is_ok());
|
|
||||||
assert_eq!(
|
|
||||||
Err(CreationError::Negative),
|
|
||||||
PositiveNonzeroInteger::new(-10)
|
|
||||||
);
|
|
||||||
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum CreationError {
|
|
||||||
Negative,
|
|
||||||
Zero,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {}
|
|
Loading…
Reference in New Issue