mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-25 00:00:05 +03:00
feat: remove advanced_errs
This commit is contained in:
parent
4bebdb5f02
commit
b609f0431c
|
@ -1,98 +0,0 @@
|
||||||
// advanced_errs1.rs
|
|
||||||
|
|
||||||
// Remember back in errors6, we had multiple mapping functions so that we
|
|
||||||
// could translate lower-level errors into our custom error type using
|
|
||||||
// `map_err()`? What if we could use the `?` operator directly instead?
|
|
||||||
|
|
||||||
// Make this code compile! Execute `rustlings hint advanced_errs1` for
|
|
||||||
// hints :)
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
// This is a custom error type that we will be using in the `FromStr`
|
|
||||||
// implementation.
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum ParsePosNonzeroError {
|
|
||||||
Creation(CreationError),
|
|
||||||
ParseInt(ParseIntError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CreationError> for ParsePosNonzeroError {
|
|
||||||
fn from(e: CreationError) -> Self {
|
|
||||||
// TODO: complete this implementation so that the `?` operator will
|
|
||||||
// work for `CreationError`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: implement another instance of the `From` trait here so that the
|
|
||||||
// `?` operator will work in the other place in the `FromStr`
|
|
||||||
// implementation below.
|
|
||||||
|
|
||||||
// Don't change anything below this line.
|
|
||||||
|
|
||||||
impl FromStr for PositiveNonzeroInteger {
|
|
||||||
type Err = ParsePosNonzeroError;
|
|
||||||
fn from_str(s: &str) -> Result<PositiveNonzeroInteger, Self::Err> {
|
|
||||||
let x: i64 = s.parse()?;
|
|
||||||
Ok(PositiveNonzeroInteger::new(x)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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!(
|
|
||||||
PositiveNonzeroInteger::from_str("not a number"),
|
|
||||||
Err(ParsePosNonzeroError::ParseInt(_))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_negative() {
|
|
||||||
assert_eq!(
|
|
||||||
PositiveNonzeroInteger::from_str("-555"),
|
|
||||||
Err(ParsePosNonzeroError::Creation(CreationError::Negative))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_zero() {
|
|
||||||
assert_eq!(
|
|
||||||
PositiveNonzeroInteger::from_str("0"),
|
|
||||||
Err(ParsePosNonzeroError::Creation(CreationError::Zero))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_positive() {
|
|
||||||
let x = PositiveNonzeroInteger::new(42);
|
|
||||||
assert!(x.is_ok());
|
|
||||||
assert_eq!(PositiveNonzeroInteger::from_str("42"), Ok(x.unwrap()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,202 +0,0 @@
|
||||||
// advanced_errs2.rs
|
|
||||||
|
|
||||||
// This exercise demonstrates a few traits that are useful for custom error
|
|
||||||
// types to implement, especially so that other code can consume the custom
|
|
||||||
// error type more usefully.
|
|
||||||
|
|
||||||
// Make this compile, and make the tests pass!
|
|
||||||
// Execute `rustlings hint advanced_errs2` for hints.
|
|
||||||
|
|
||||||
// Steps:
|
|
||||||
// 1. Implement a missing trait so that `main()` will compile.
|
|
||||||
// 2. Complete the partial implementation of `From` for
|
|
||||||
// `ParseClimateError`.
|
|
||||||
// 3. Handle the missing error cases in the `FromStr` implementation for
|
|
||||||
// `Climate`.
|
|
||||||
// 4. Complete the partial implementation of `Display` for
|
|
||||||
// `ParseClimateError`.
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
use std::num::{ParseFloatError, ParseIntError};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
// This is the custom error type that we will be using for the parser for
|
|
||||||
// `Climate`.
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
enum ParseClimateError {
|
|
||||||
Empty,
|
|
||||||
BadLen,
|
|
||||||
NoCity,
|
|
||||||
ParseInt(ParseIntError),
|
|
||||||
ParseFloat(ParseFloatError),
|
|
||||||
}
|
|
||||||
|
|
||||||
// This `From` implementation allows the `?` operator to work on
|
|
||||||
// `ParseIntError` values.
|
|
||||||
impl From<ParseIntError> for ParseClimateError {
|
|
||||||
fn from(e: ParseIntError) -> Self {
|
|
||||||
Self::ParseInt(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This `From` implementation allows the `?` operator to work on
|
|
||||||
// `ParseFloatError` values.
|
|
||||||
impl From<ParseFloatError> for ParseClimateError {
|
|
||||||
fn from(e: ParseFloatError) -> Self {
|
|
||||||
// TODO: Complete this function
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement a missing trait so that `main()` below will compile. It
|
|
||||||
// is not necessary to implement any methods inside the missing trait.
|
|
||||||
|
|
||||||
// The `Display` trait allows for other code to obtain the error formatted
|
|
||||||
// as a user-visible string.
|
|
||||||
impl Display for ParseClimateError {
|
|
||||||
// TODO: Complete this function so that it produces the correct strings
|
|
||||||
// for each error variant.
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
// Imports the variants to make the following code more compact.
|
|
||||||
use ParseClimateError::*;
|
|
||||||
match self {
|
|
||||||
NoCity => write!(f, "no city name"),
|
|
||||||
ParseFloat(e) => write!(f, "error parsing temperature: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
struct Climate {
|
|
||||||
city: String,
|
|
||||||
year: u32,
|
|
||||||
temp: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parser for `Climate`.
|
|
||||||
// 1. Split the input string into 3 fields: city, year, temp.
|
|
||||||
// 2. Return an error if the string is empty or has the wrong number of
|
|
||||||
// fields.
|
|
||||||
// 3. Return an error if the city name is empty.
|
|
||||||
// 4. Parse the year as a `u32` and return an error if that fails.
|
|
||||||
// 5. Parse the temp as a `f32` and return an error if that fails.
|
|
||||||
// 6. Return an `Ok` value containing the completed `Climate` value.
|
|
||||||
impl FromStr for Climate {
|
|
||||||
type Err = ParseClimateError;
|
|
||||||
// TODO: Complete this function by making it handle the missing error
|
|
||||||
// cases.
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let v: Vec<_> = s.split(',').collect();
|
|
||||||
let (city, year, temp) = match &v[..] {
|
|
||||||
[city, year, temp] => (city.to_string(), year, temp),
|
|
||||||
_ => return Err(ParseClimateError::BadLen),
|
|
||||||
};
|
|
||||||
let year: u32 = year.parse()?;
|
|
||||||
let temp: f32 = temp.parse()?;
|
|
||||||
Ok(Climate { city, year, temp })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't change anything below this line (other than to enable ignored
|
|
||||||
// tests).
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
println!("{:?}", "Hong Kong,1999,25.7".parse::<Climate>()?);
|
|
||||||
println!("{:?}", "".parse::<Climate>()?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
#[test]
|
|
||||||
fn test_empty() {
|
|
||||||
let res = "".parse::<Climate>();
|
|
||||||
assert_eq!(res, Err(ParseClimateError::Empty));
|
|
||||||
assert_eq!(res.unwrap_err().to_string(), "empty input");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_short() {
|
|
||||||
let res = "Boston,1991".parse::<Climate>();
|
|
||||||
assert_eq!(res, Err(ParseClimateError::BadLen));
|
|
||||||
assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_long() {
|
|
||||||
let res = "Paris,1920,17.2,extra".parse::<Climate>();
|
|
||||||
assert_eq!(res, Err(ParseClimateError::BadLen));
|
|
||||||
assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_no_city() {
|
|
||||||
let res = ",1997,20.5".parse::<Climate>();
|
|
||||||
assert_eq!(res, Err(ParseClimateError::NoCity));
|
|
||||||
assert_eq!(res.unwrap_err().to_string(), "no city name");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_parse_int_neg() {
|
|
||||||
let res = "Barcelona,-25,22.3".parse::<Climate>();
|
|
||||||
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
if let ParseClimateError::ParseInt(ref inner) = err {
|
|
||||||
assert_eq!(
|
|
||||||
err.to_string(),
|
|
||||||
format!("error parsing year: {}", inner.to_string())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_parse_int_bad() {
|
|
||||||
let res = "Beijing,foo,15.0".parse::<Climate>();
|
|
||||||
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
if let ParseClimateError::ParseInt(ref inner) = err {
|
|
||||||
assert_eq!(
|
|
||||||
err.to_string(),
|
|
||||||
format!("error parsing year: {}", inner.to_string())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_parse_float() {
|
|
||||||
let res = "Manila,2001,bar".parse::<Climate>();
|
|
||||||
assert!(matches!(res, Err(ParseClimateError::ParseFloat(_))));
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
if let ParseClimateError::ParseFloat(ref inner) = err {
|
|
||||||
assert_eq!(
|
|
||||||
err.to_string(),
|
|
||||||
format!("error parsing temperature: {}", inner.to_string())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_parse_good() {
|
|
||||||
let res = "Munich,2015,23.1".parse::<Climate>();
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
Ok(Climate {
|
|
||||||
city: "Munich".to_string(),
|
|
||||||
year: 2015,
|
|
||||||
temp: 23.1,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn test_downcast() {
|
|
||||||
let res = "São Paulo,-21,28.5".parse::<Climate>();
|
|
||||||
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
let inner: Option<&(dyn Error + 'static)> = err.source();
|
|
||||||
assert!(inner.is_some());
|
|
||||||
assert!(inner.unwrap().is::<ParseIntError>());
|
|
||||||
}
|
|
||||||
}
|
|
52
info.toml
52
info.toml
|
@ -1061,55 +1061,3 @@ path = "exercises/conversions/as_ref_mut.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
hint = """
|
hint = """
|
||||||
Add AsRef<str> as a trait bound to the functions."""
|
Add AsRef<str> as a trait bound to the functions."""
|
||||||
|
|
||||||
# ADVANCED ERRORS
|
|
||||||
|
|
||||||
[[exercises]]
|
|
||||||
name = "advanced_errs1"
|
|
||||||
path = "exercises/advanced_errors/advanced_errs1.rs"
|
|
||||||
mode = "test"
|
|
||||||
hint = """
|
|
||||||
This exercise uses an updated version of the code in errors6. The parsing
|
|
||||||
code is now in an implementation of the `FromStr` trait. Note that the
|
|
||||||
parsing code uses `?` directly, without any calls to `map_err()`. There is
|
|
||||||
one partial implementation of the `From` trait example that you should
|
|
||||||
complete.
|
|
||||||
|
|
||||||
Details: The `?` operator calls `From::from()` on the error type to convert
|
|
||||||
it to the error type of the return type of the surrounding function.
|
|
||||||
|
|
||||||
Hint: You will need to write another implementation of `From` that has a
|
|
||||||
different input type.
|
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
|
||||||
name = "advanced_errs2"
|
|
||||||
path = "exercises/advanced_errors/advanced_errs2.rs"
|
|
||||||
mode = "test"
|
|
||||||
hint = """
|
|
||||||
This exercise demonstrates a few traits that are useful for custom error
|
|
||||||
types to implement. These traits make it easier for other code to consume
|
|
||||||
the custom error type.
|
|
||||||
|
|
||||||
Follow the steps in the comment near the top of the file. You will have to
|
|
||||||
supply a missing trait implementation, and complete a few incomplete ones.
|
|
||||||
|
|
||||||
You may find these pages to be helpful references:
|
|
||||||
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/define_error_type.html
|
|
||||||
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
|
|
||||||
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/wrap_error.html
|
|
||||||
|
|
||||||
Hint: What trait must our error type have for `main()` to return the return
|
|
||||||
type that it returns?
|
|
||||||
|
|
||||||
Another hint: It's not necessary to implement any methods inside the missing
|
|
||||||
trait. (Some methods have default implementations that are supplied by the
|
|
||||||
trait.)
|
|
||||||
|
|
||||||
Another hint: Consult the tests to determine which error variants (and which
|
|
||||||
error message text) to produce for certain error conditions.
|
|
||||||
|
|
||||||
Challenge: There is one test that is marked `#[ignore]`. Can you supply the
|
|
||||||
missing code that will make it pass? You may want to consult the standard
|
|
||||||
library documentation for a certain trait for more hints.
|
|
||||||
"""
|
|
||||||
|
|
Loading…
Reference in a new issue