mirror of
https://github.com/rust-lang/rustlings.git
synced 2026-01-10 12:49:18 +00:00
Merge branch 'main' of github.com:rust-lang/rustlings into main
This commit is contained in:
commit
d34e3379a3
@ -966,6 +966,78 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"content"
|
"content"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Weilet",
|
||||||
|
"name": "Weilet",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/32561597?v=4",
|
||||||
|
"profile": "https://weilet.me",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Millione",
|
||||||
|
"name": "LIU JIE",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/38575932?v=4",
|
||||||
|
"profile": "https://github.com/Millione",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "abusch",
|
||||||
|
"name": "Antoine Büsch",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/506344?v=4",
|
||||||
|
"profile": "https://github.com/abusch",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "frogtd",
|
||||||
|
"name": "frogtd",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/31412003?v=4",
|
||||||
|
"profile": "https://frogtd.com/",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "EmisonLu",
|
||||||
|
"name": "Zhenghao Lu",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/54395432?v=4",
|
||||||
|
"profile": "https://github.com/EmisonLu",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "fredr",
|
||||||
|
"name": "Fredrik Enestad",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/762956?v=4",
|
||||||
|
"profile": "https://soundtrackyourbrand.com",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "xuesongbj",
|
||||||
|
"name": "xuesong",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/18476085?v=4",
|
||||||
|
"profile": "http://xuesong.pydevops.com",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "MpdWalsh",
|
||||||
|
"name": "Michael Walsh",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/48160144?v=4",
|
||||||
|
"profile": "https://github.com/MpdWalsh",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 8,
|
"contributorsPerLine": 8,
|
||||||
|
|||||||
26
CHANGELOG.md
26
CHANGELOG.md
@ -1,3 +1,29 @@
|
|||||||
|
<a name="4.6.0"></a>
|
||||||
|
## 4.6.0 (2021-09-25)
|
||||||
|
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
* add advanced_errs2 ([abd6b70c](https://github.com/rust-lang/rustlings/commit/abd6b70c72dc6426752ff41f09160b839e5c449e))
|
||||||
|
* add advanced_errs1 ([882d535b](https://github.com/rust-lang/rustlings/commit/882d535ba8628d5e0b37e8664b3e2f26260b2671))
|
||||||
|
* Add a farewell message when quitting `watch` ([1caef0b4](https://github.com/rust-lang/rustlings/commit/1caef0b43494c8b8cdd6c9260147e70d510f1aca))
|
||||||
|
* add more watch commands ([a7dc080b](https://github.com/rust-lang/rustlings/commit/a7dc080b95e49146fbaafe6922a6de2f8cb1582a), closes [#842](https://github.com/rust-lang/rustlings/issues/842))
|
||||||
|
* **modules:** update exercises, add modules3 (#822) ([dfd2fab4](https://github.com/rust-lang/rustlings/commit/dfd2fab4f33d1bf59e2e5ee03123c0c9a67a9481))
|
||||||
|
* **quiz1:** add default function name in comment (#838) ([0a11bad7](https://github.com/rust-lang/rustlings/commit/0a11bad71402b5403143d642f439f57931278c07))
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
* Correct small typo in exercises/conversions/from_str.rs ([86cc8529](https://github.com/rust-lang/rustlings/commit/86cc85295ae36948963ae52882e285d7e3e29323))
|
||||||
|
* **cli:** typo in exercise.rs (#848) ([06d5c097](https://github.com/rust-lang/rustlings/commit/06d5c0973a3dffa3c6c6f70acb775d4c6630323c))
|
||||||
|
* **from_str, try_from_into:** custom error types ([2dc93cad](https://github.com/rust-lang/rustlings/commit/2dc93caddad43821743e4903d89b355df58d7a49))
|
||||||
|
* **modules2:** fix typo (#835) ([1c3beb0a](https://github.com/rust-lang/rustlings/commit/1c3beb0a59178c950dc05fe8ee2346b017429ae0))
|
||||||
|
* **move_semantics5:**
|
||||||
|
* change &mut *y to &mut x (#814) ([d75759e8](https://github.com/rust-lang/rustlings/commit/d75759e829fdcd64ef071cf4b6eae2a011a7718b))
|
||||||
|
* Clarify instructions ([df25684c](https://github.com/rust-lang/rustlings/commit/df25684cb79f8413915e00b5efef29369849cef1))
|
||||||
|
* **quiz1:** Fix inconsistent wording (#826) ([03131a3d](https://github.com/rust-lang/rustlings/commit/03131a3d35d9842598150f9da817f7cc26e2669a))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="4.5.0"></a>
|
<a name="4.5.0"></a>
|
||||||
## 4.5.0 (2021-07-07)
|
## 4.5.0 (2021-07-07)
|
||||||
|
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -541,7 +541,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustlings"
|
name = "rustlings"
|
||||||
version = "4.5.0"
|
version = "4.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustlings"
|
name = "rustlings"
|
||||||
version = "4.5.0"
|
version = "4.6.0"
|
||||||
authors = ["anastasie <ana@ana.st>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com>"]
|
authors = ["anastasie <ana@ana.st>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
argh = "0.1.4"
|
argh = "0.1.4"
|
||||||
|
|||||||
16
README.md
16
README.md
@ -1,5 +1,5 @@
|
|||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
[](#contributors-)
|
[](#contributors-)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
# rustlings 🦀❤️
|
# rustlings 🦀❤️
|
||||||
@ -60,8 +60,8 @@ When you get a permission denied message then you have to exclude the directory
|
|||||||
Basically: Clone the repository at the latest tag, run `cargo install`.
|
Basically: Clone the repository at the latest tag, run `cargo install`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 4.5.0)
|
# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 4.6.0)
|
||||||
git clone -b 4.5.0 --depth 1 https://github.com/rust-lang/rustlings
|
git clone -b 4.6.0 --depth 1 https://github.com/rust-lang/rustlings
|
||||||
cd rustlings
|
cd rustlings
|
||||||
cargo install --force --path .
|
cargo install --force --path .
|
||||||
```
|
```
|
||||||
@ -307,6 +307,16 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<td align="center"><a href="https://github.com/anuk909"><img src="https://avatars.githubusercontent.com/u/34924662?v=4?s=100" width="100px;" alt=""/><br /><sub><b>anuk909</b></sub></a><br /><a href="#content-anuk909" title="Content">🖋</a> <a href="https://github.com/rust-lang/rustlings/commits?author=anuk909" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/anuk909"><img src="https://avatars.githubusercontent.com/u/34924662?v=4?s=100" width="100px;" alt=""/><br /><sub><b>anuk909</b></sub></a><br /><a href="#content-anuk909" title="Content">🖋</a> <a href="https://github.com/rust-lang/rustlings/commits?author=anuk909" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://granddaifuku.com/"><img src="https://avatars.githubusercontent.com/u/49578068?v=4?s=100" width="100px;" alt=""/><br /><sub><b>granddaifuku</b></sub></a><br /><a href="#content-granddaifuku" title="Content">🖋</a></td>
|
<td align="center"><a href="https://granddaifuku.com/"><img src="https://avatars.githubusercontent.com/u/49578068?v=4?s=100" width="100px;" alt=""/><br /><sub><b>granddaifuku</b></sub></a><br /><a href="#content-granddaifuku" title="Content">🖋</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://weilet.me"><img src="https://avatars.githubusercontent.com/u/32561597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Weilet</b></sub></a><br /><a href="#content-Weilet" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Millione"><img src="https://avatars.githubusercontent.com/u/38575932?v=4?s=100" width="100px;" alt=""/><br /><sub><b>LIU JIE</b></sub></a><br /><a href="#content-Millione" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/abusch"><img src="https://avatars.githubusercontent.com/u/506344?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Antoine Büsch</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=abusch" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://frogtd.com/"><img src="https://avatars.githubusercontent.com/u/31412003?v=4?s=100" width="100px;" alt=""/><br /><sub><b>frogtd</b></sub></a><br /><a href="#content-frogtd" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/EmisonLu"><img src="https://avatars.githubusercontent.com/u/54395432?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zhenghao Lu</b></sub></a><br /><a href="#content-EmisonLu" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="https://soundtrackyourbrand.com"><img src="https://avatars.githubusercontent.com/u/762956?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fredrik Enestad</b></sub></a><br /><a href="#content-fredr" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="http://xuesong.pydevops.com"><img src="https://avatars.githubusercontent.com/u/18476085?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xuesong</b></sub></a><br /><a href="#content-xuesongbj" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/MpdWalsh"><img src="https://avatars.githubusercontent.com/u/48160144?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Walsh</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=MpdWalsh" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- markdownlint-restore -->
|
<!-- markdownlint-restore -->
|
||||||
|
|||||||
98
exercises/advanced_errors/advanced_errs1.rs
Normal file
98
exercises/advanced_errors/advanced_errs1.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// 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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
202
exercises/advanced_errors/advanced_errs2.rs
Normal file
202
exercises/advanced_errors/advanced_errs2.rs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
// 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>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,16 +1,31 @@
|
|||||||
// This does practically the same thing that TryFrom<&str> does.
|
// from_str.rs
|
||||||
|
// This is similar to from_into.rs, but this time we'll implement `FromStr`
|
||||||
|
// and return errors instead of falling back to a default value.
|
||||||
// Additionally, upon implementing FromStr, you can use the `parse` method
|
// Additionally, upon implementing FromStr, you can use the `parse` method
|
||||||
// on strings to generate an object of the implementor type.
|
// on strings to generate an object of the implementor type.
|
||||||
// You can read more about it at https://doc.rust-lang.org/std/str/trait.FromStr.html
|
// You can read more about it at https://doc.rust-lang.org/std/str/trait.FromStr.html
|
||||||
use std::error;
|
use std::num::ParseIntError;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: usize,
|
age: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We will use this error type for the `FromStr` implementation.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum ParsePersonError {
|
||||||
|
// Empty input string
|
||||||
|
Empty,
|
||||||
|
// Incorrect number of fields
|
||||||
|
BadLen,
|
||||||
|
// Empty name field
|
||||||
|
NoName,
|
||||||
|
// Wrapped error from parse::<usize>()
|
||||||
|
ParseInt(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
||||||
// Steps:
|
// Steps:
|
||||||
@ -24,7 +39,7 @@ struct Person {
|
|||||||
// If everything goes well, then return a Result of a Person object
|
// If everything goes well, then return a Result of a Person object
|
||||||
|
|
||||||
impl FromStr for Person {
|
impl FromStr for Person {
|
||||||
type Err = Box<dyn error::Error>;
|
type Err = ParsePersonError;
|
||||||
fn from_str(s: &str) -> Result<Person, Self::Err> {
|
fn from_str(s: &str) -> Result<Person, Self::Err> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,7 +55,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_input() {
|
fn empty_input() {
|
||||||
assert!("".parse::<Person>().is_err());
|
assert_eq!("".parse::<Person>(), Err(ParsePersonError::Empty));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn good_input() {
|
fn good_input() {
|
||||||
@ -52,41 +67,56 @@ mod tests {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_age() {
|
fn missing_age() {
|
||||||
assert!("John,".parse::<Person>().is_err());
|
assert!(matches!(
|
||||||
|
"John,".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::ParseInt(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_age() {
|
fn invalid_age() {
|
||||||
assert!("John,twenty".parse::<Person>().is_err());
|
assert!(matches!(
|
||||||
|
"John,twenty".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::ParseInt(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_comma_and_age() {
|
fn missing_comma_and_age() {
|
||||||
assert!("John".parse::<Person>().is_err());
|
assert_eq!("John".parse::<Person>(), Err(ParsePersonError::BadLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_name() {
|
fn missing_name() {
|
||||||
assert!(",1".parse::<Person>().is_err());
|
assert_eq!(",1".parse::<Person>(), Err(ParsePersonError::NoName));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_name_and_age() {
|
fn missing_name_and_age() {
|
||||||
assert!(",".parse::<Person>().is_err());
|
assert!(matches!(
|
||||||
|
",".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_name_and_invalid_age() {
|
fn missing_name_and_invalid_age() {
|
||||||
assert!(",one".parse::<Person>().is_err());
|
assert!(matches!(
|
||||||
|
",one".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trailing_comma() {
|
fn trailing_comma() {
|
||||||
assert!("John,32,".parse::<Person>().is_err());
|
assert_eq!("John,32,".parse::<Person>(), Err(ParsePersonError::BadLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trailing_comma_and_some_string() {
|
fn trailing_comma_and_some_string() {
|
||||||
assert!("John,32,man".parse::<Person>().is_err());
|
assert_eq!(
|
||||||
|
"John,32,man".parse::<Person>(),
|
||||||
|
Err(ParsePersonError::BadLen)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
|
// try_from_into.rs
|
||||||
// TryFrom is a simple and safe type conversion that may fail in a controlled way under some circumstances.
|
// TryFrom is a simple and safe type conversion that may fail in a controlled way under some circumstances.
|
||||||
// Basically, this is the same as From. The main difference is that this should return a Result type
|
// Basically, this is the same as From. The main difference is that this should return a Result type
|
||||||
// instead of the target type itself.
|
// instead of the target type itself.
|
||||||
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::error;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct Color {
|
struct Color {
|
||||||
@ -12,12 +12,21 @@ struct Color {
|
|||||||
blue: u8,
|
blue: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We will use this error type for these `TryFrom` conversions.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum IntoColorError {
|
||||||
|
// Incorrect length of slice
|
||||||
|
BadLen,
|
||||||
|
// Integer conversion error
|
||||||
|
IntConversion,
|
||||||
|
}
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
||||||
// Your task is to complete this implementation
|
// Your task is to complete this implementation
|
||||||
// and return an Ok result of inner type Color.
|
// and return an Ok result of inner type Color.
|
||||||
// You need to create an implementation for a tuple of three integers,
|
// You need to create an implementation for a tuple of three integers,
|
||||||
// an array of three integers and a slice of integers.
|
// an array of three integers, and a slice of integers.
|
||||||
//
|
//
|
||||||
// Note that the implementation for tuple and array will be checked at compile time,
|
// Note that the implementation for tuple and array will be checked at compile time,
|
||||||
// but the slice implementation needs to check the slice length!
|
// but the slice implementation needs to check the slice length!
|
||||||
@ -25,20 +34,23 @@ struct Color {
|
|||||||
|
|
||||||
// Tuple implementation
|
// Tuple implementation
|
||||||
impl TryFrom<(i16, i16, i16)> for Color {
|
impl TryFrom<(i16, i16, i16)> for Color {
|
||||||
type Error = Box<dyn error::Error>;
|
type Error = IntoColorError;
|
||||||
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {}
|
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array implementation
|
// Array implementation
|
||||||
impl TryFrom<[i16; 3]> for Color {
|
impl TryFrom<[i16; 3]> for Color {
|
||||||
type Error = Box<dyn error::Error>;
|
type Error = IntoColorError;
|
||||||
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {}
|
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slice implementation
|
// Slice implementation
|
||||||
impl TryFrom<&[i16]> for Color {
|
impl TryFrom<&[i16]> for Color {
|
||||||
type Error = Box<dyn error::Error>;
|
type Error = IntoColorError;
|
||||||
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {}
|
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -46,15 +58,15 @@ fn main() {
|
|||||||
let c1 = Color::try_from((183, 65, 14));
|
let c1 = Color::try_from((183, 65, 14));
|
||||||
println!("{:?}", c1);
|
println!("{:?}", c1);
|
||||||
|
|
||||||
// Since From is implemented for Color, we should be able to use Into
|
// Since TryFrom is implemented for Color, we should be able to use TryInto
|
||||||
let c2: Result<Color, _> = [183, 65, 14].try_into();
|
let c2: Result<Color, _> = [183, 65, 14].try_into();
|
||||||
println!("{:?}", c2);
|
println!("{:?}", c2);
|
||||||
|
|
||||||
let v = vec![183, 65, 14];
|
let v = vec![183, 65, 14];
|
||||||
// With slice we should use `from` function
|
// With slice we should use `try_from` function
|
||||||
let c3 = Color::try_from(&v[..]);
|
let c3 = Color::try_from(&v[..]);
|
||||||
println!("{:?}", c3);
|
println!("{:?}", c3);
|
||||||
// or take slice within round brackets and use Into
|
// or take slice within round brackets and use TryInto
|
||||||
let c4: Result<Color, _> = (&v[..]).try_into();
|
let c4: Result<Color, _> = (&v[..]).try_into();
|
||||||
println!("{:?}", c4);
|
println!("{:?}", c4);
|
||||||
}
|
}
|
||||||
@ -65,15 +77,24 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_out_of_range_positive() {
|
fn test_tuple_out_of_range_positive() {
|
||||||
assert!(Color::try_from((256, 1000, 10000)).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from((256, 1000, 10000)),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_out_of_range_negative() {
|
fn test_tuple_out_of_range_negative() {
|
||||||
assert!(Color::try_from((-1, -10, -256)).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from((-1, -10, -256)),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_sum() {
|
fn test_tuple_sum() {
|
||||||
assert!(Color::try_from((-1, 255, 255)).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from((-1, 255, 255)),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_correct() {
|
fn test_tuple_correct() {
|
||||||
@ -91,17 +112,17 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_array_out_of_range_positive() {
|
fn test_array_out_of_range_positive() {
|
||||||
let c: Result<Color, _> = [1000, 10000, 256].try_into();
|
let c: Result<Color, _> = [1000, 10000, 256].try_into();
|
||||||
assert!(c.is_err());
|
assert_eq!(c, Err(IntoColorError::IntConversion));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_out_of_range_negative() {
|
fn test_array_out_of_range_negative() {
|
||||||
let c: Result<Color, _> = [-10, -256, -1].try_into();
|
let c: Result<Color, _> = [-10, -256, -1].try_into();
|
||||||
assert!(c.is_err());
|
assert_eq!(c, Err(IntoColorError::IntConversion));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_sum() {
|
fn test_array_sum() {
|
||||||
let c: Result<Color, _> = [-1, 255, 255].try_into();
|
let c: Result<Color, _> = [-1, 255, 255].try_into();
|
||||||
assert!(c.is_err());
|
assert_eq!(c, Err(IntoColorError::IntConversion));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_correct() {
|
fn test_array_correct() {
|
||||||
@ -119,17 +140,26 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_slice_out_of_range_positive() {
|
fn test_slice_out_of_range_positive() {
|
||||||
let arr = [10000, 256, 1000];
|
let arr = [10000, 256, 1000];
|
||||||
assert!(Color::try_from(&arr[..]).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from(&arr[..]),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_out_of_range_negative() {
|
fn test_slice_out_of_range_negative() {
|
||||||
let arr = [-256, -1, -10];
|
let arr = [-256, -1, -10];
|
||||||
assert!(Color::try_from(&arr[..]).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from(&arr[..]),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_sum() {
|
fn test_slice_sum() {
|
||||||
let arr = [-1, 255, 255];
|
let arr = [-1, 255, 255];
|
||||||
assert!(Color::try_from(&arr[..]).is_err());
|
assert_eq!(
|
||||||
|
Color::try_from(&arr[..]),
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_correct() {
|
fn test_slice_correct() {
|
||||||
@ -148,11 +178,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_slice_excess_length() {
|
fn test_slice_excess_length() {
|
||||||
let v = vec![0, 0, 0, 0];
|
let v = vec![0, 0, 0, 0];
|
||||||
assert!(Color::try_from(&v[..]).is_err());
|
assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slice_insufficient_length() {
|
fn test_slice_insufficient_length() {
|
||||||
let v = vec![0, 0];
|
let v = vec![0, 0];
|
||||||
assert!(Color::try_from(&v[..]).is_err());
|
assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
// move_semantics5.rs
|
// move_semantics5.rs
|
||||||
// Make me compile only be reordering the lines in `main()`, but without
|
// Make me compile only by reordering the lines in `main()`, but without
|
||||||
// adding, changing or removing any of them.
|
// adding, changing or removing any of them.
|
||||||
// Execute `rustlings hint move_semantics5` for hints :)
|
// Execute `rustlings hint move_semantics5` for hints :)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut x = 100;
|
let mut x = 100;
|
||||||
let y = &mut x;
|
let y = &mut x;
|
||||||
|
let z = &mut x;
|
||||||
*y += 100;
|
*y += 100;
|
||||||
let z = &mut *y;
|
let z = &mut *y;
|
||||||
*z += 1000;
|
*z += 1000;
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
// the price of an order of apples given the quantity bought. No hints this time!
|
// the price of an order of apples given the quantity bought. No hints this time!
|
||||||
|
|
||||||
// Put your function here!
|
// Put your function here!
|
||||||
// fn ..... {
|
// fn calculate_apple_price {
|
||||||
|
|
||||||
fn calculate_apple_price(amount: u8) -> u8 {
|
fn calculate_apple_price(amount: u8) -> u8 {
|
||||||
if amount > 40 {
|
if amount > 40 {
|
||||||
|
|||||||
@ -18,11 +18,11 @@ impl Package {
|
|||||||
if weight_in_grams <= 0 {
|
if weight_in_grams <= 0 {
|
||||||
// Something goes here...
|
// Something goes here...
|
||||||
} else {
|
} else {
|
||||||
return Package {
|
Package {
|
||||||
sender_country,
|
sender_country,
|
||||||
recipient_country,
|
recipient_country,
|
||||||
weight_in_grams,
|
weight_in_grams,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ mod tests {
|
|||||||
let sender_country = String::from("Spain");
|
let sender_country = String::from("Spain");
|
||||||
let recipient_country = String::from("Spain");
|
let recipient_country = String::from("Spain");
|
||||||
|
|
||||||
let cents_per_gram = ???;
|
let cents_per_gram = 3;
|
||||||
|
|
||||||
let package = Package::new(sender_country, recipient_country, 1500);
|
let package = Package::new(sender_country, recipient_country, 1500);
|
||||||
|
|
||||||
|
|||||||
@ -29,12 +29,12 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_FooBar() {
|
fn is_foo_bar() {
|
||||||
assert_eq!(String::from("Foo").append_bar(), String::from("FooBar"));
|
assert_eq!(String::from("Foo").append_bar(), String::from("FooBar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_BarBar() {
|
fn is_bar_bar() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
String::from("").append_bar().append_bar(),
|
String::from("").append_bar().append_bar(),
|
||||||
String::from("BarBar")
|
String::from("BarBar")
|
||||||
|
|||||||
97
info.toml
97
info.toml
@ -364,7 +364,7 @@ mode = "compile"
|
|||||||
hint = """
|
hint = """
|
||||||
The delicious_snacks module is trying to present an external interface that is
|
The delicious_snacks module is trying to present an external interface that is
|
||||||
different than its internal structure (the `fruits` and `veggies` modules and
|
different than its internal structure (the `fruits` and `veggies` modules and
|
||||||
associated constants). Complete the `use` statemants to fit the uses in main and
|
associated constants). Complete the `use` statements to fit the uses in main and
|
||||||
find the one keyword missing for both constants."""
|
find the one keyword missing for both constants."""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
@ -941,6 +941,27 @@ mode = "test"
|
|||||||
hint = """
|
hint = """
|
||||||
Follow the steps provided right before the `From` implementation"""
|
Follow the steps provided right before the `From` implementation"""
|
||||||
|
|
||||||
|
[[exercises]]
|
||||||
|
name = "from_str"
|
||||||
|
path = "exercises/conversions/from_str.rs"
|
||||||
|
mode = "test"
|
||||||
|
hint = """
|
||||||
|
The implementation of FromStr should return an Ok with a Person object,
|
||||||
|
or an Err with an error if the string is not valid.
|
||||||
|
|
||||||
|
This is almost like the `from_into` exercise, but returning errors instead
|
||||||
|
of falling back to a default value.
|
||||||
|
|
||||||
|
Hint: Look at the test cases to see which error variants to return.
|
||||||
|
|
||||||
|
Another hint: You can use the `map_err` method of `Result` with a function
|
||||||
|
or a closure to wrap the error from `parse::<usize>`.
|
||||||
|
|
||||||
|
Yet another hint: If you would like to propagate errors by using the `?`
|
||||||
|
operator in your solution, you might want to look at
|
||||||
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
|
||||||
|
"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "try_from_into"
|
name = "try_from_into"
|
||||||
path = "exercises/conversions/try_from_into.rs"
|
path = "exercises/conversions/try_from_into.rs"
|
||||||
@ -949,17 +970,19 @@ hint = """
|
|||||||
Follow the steps provided right before the `TryFrom` implementation.
|
Follow the steps provided right before the `TryFrom` implementation.
|
||||||
You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
||||||
|
|
||||||
You might want to look back at the exercise errors5 (or its hints) to remind
|
Hint: Is there an implementation of `TryFrom` in the standard library that
|
||||||
yourself about how `Box<dyn Error>` works.
|
can both do the required integer conversion and check the range of the input?
|
||||||
|
|
||||||
If you're trying to return a string as an error, note that neither `str`
|
Another hint: Look at the test cases to see which error variants to return.
|
||||||
nor `String` implements `error::Error`. However, there is an implementation
|
|
||||||
of `From<&str>` for `Box<dyn Error>`. This means you can use `.into()` or
|
|
||||||
the `?` operator to convert your string into the correct error type.
|
|
||||||
|
|
||||||
If you're having trouble with using the `?` operator to convert an error string,
|
Yet another hint: You can use the `map_err` or `or` methods of `Result` to
|
||||||
recall that `?` works to convert `Err(something)` into the appropriate error
|
convert errors.
|
||||||
type for returning from the function."""
|
|
||||||
|
Yet another hint: If you would like to propagate errors by using the `?`
|
||||||
|
operator in your solution, you might want to look at
|
||||||
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
|
||||||
|
|
||||||
|
Challenge: Can you make the `TryFrom` implementations generic over many integer types?"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "as_ref_mut"
|
name = "as_ref_mut"
|
||||||
@ -968,14 +991,54 @@ 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]]
|
[[exercises]]
|
||||||
name = "from_str"
|
name = "advanced_errs1"
|
||||||
path = "exercises/conversions/from_str.rs"
|
path = "exercises/advanced_errors/advanced_errs1.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
hint = """
|
hint = """
|
||||||
The implementation of FromStr should return an Ok with a Person object,
|
This exercise uses an updated version of the code in errors6. The parsing
|
||||||
or an Err with an error if the string is not valid.
|
code is now in an implementation of the `FromStr` trait. Note that the
|
||||||
This is almost like the `try_from_into` exercise.
|
parsing code uses `?` directly, without any calls to `map_err()`. There is
|
||||||
|
one partial implementation of the `From` trait example that you should
|
||||||
|
complete.
|
||||||
|
|
||||||
If you're having trouble with returning the correct error type, see the
|
Details: The `?` operator calls `From::from()` on the error type to convert
|
||||||
hints for try_from_into."""
|
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.
|
||||||
|
"""
|
||||||
|
|||||||
@ -133,7 +133,7 @@ path = "{}.rs""#,
|
|||||||
"Failed to write 📎 Clippy 📎 Cargo.toml file."
|
"Failed to write 📎 Clippy 📎 Cargo.toml file."
|
||||||
};
|
};
|
||||||
fs::write(CLIPPY_CARGO_TOML_PATH, cargo_toml).expect(cargo_toml_error_msg);
|
fs::write(CLIPPY_CARGO_TOML_PATH, cargo_toml).expect(cargo_toml_error_msg);
|
||||||
// To support the ability to run the clipy exercises, build
|
// To support the ability to run the clippy exercises, build
|
||||||
// an executable, in addition to running clippy. With a
|
// an executable, in addition to running clippy. With a
|
||||||
// compilation failure, this would silently fail. But we expect
|
// compilation failure, this would silently fail. But we expect
|
||||||
// clippy to reflect the same failure while compiling later.
|
// clippy to reflect the same failure while compiling later.
|
||||||
|
|||||||
131
src/main.rs
131
src/main.rs
@ -10,7 +10,8 @@ use std::fs;
|
|||||||
use std::io::{self, prelude::*};
|
use std::io::{self, prelude::*};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::mpsc::{channel, RecvTimeoutError};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -23,7 +24,7 @@ mod run;
|
|||||||
mod verify;
|
mod verify;
|
||||||
|
|
||||||
// In sync with crate version
|
// In sync with crate version
|
||||||
const VERSION: &str = "4.5.0";
|
const VERSION: &str = "4.6.0";
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
|
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
|
||||||
@ -216,8 +217,8 @@ fn main() {
|
|||||||
verify(&exercises, verbose).unwrap_or_else(|_| std::process::exit(1));
|
verify(&exercises, verbose).unwrap_or_else(|_| std::process::exit(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Subcommands::Watch(_subargs) => {
|
Subcommands::Watch(_subargs) => match watch(&exercises, verbose) {
|
||||||
if let Err(e) = watch(&exercises, verbose) {
|
Err(e) => {
|
||||||
println!(
|
println!(
|
||||||
"Error: Could not watch your progress. Error message was {:?}.",
|
"Error: Could not watch your progress. Error message was {:?}.",
|
||||||
e
|
e
|
||||||
@ -225,57 +226,80 @@ fn main() {
|
|||||||
println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
|
println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
println!(
|
Ok(WatchStatus::Finished) => {
|
||||||
"{emoji} All exercises completed! {emoji}",
|
println!(
|
||||||
emoji = Emoji("🎉", "★")
|
"{emoji} All exercises completed! {emoji}",
|
||||||
);
|
emoji = Emoji("🎉", "★")
|
||||||
println!();
|
);
|
||||||
println!("+----------------------------------------------------+");
|
println!();
|
||||||
println!("| You made it to the Fe-nish line! |");
|
println!("+----------------------------------------------------+");
|
||||||
println!("+-------------------------- ------------------------+");
|
println!("| You made it to the Fe-nish line! |");
|
||||||
println!(" \\/ ");
|
println!("+-------------------------- ------------------------+");
|
||||||
println!(" ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ ");
|
println!(" \\/ ");
|
||||||
println!(" ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ");
|
println!(" ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ ");
|
||||||
println!(" ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ");
|
println!(" ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ");
|
||||||
println!(" ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ ");
|
println!(" ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ");
|
||||||
println!(" ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ ");
|
println!(" ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ ");
|
||||||
println!(" ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ ");
|
println!(" ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ ");
|
||||||
println!(" ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ ");
|
println!(" ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ ");
|
||||||
println!(" ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ ");
|
println!(" ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ ");
|
||||||
println!(" ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ");
|
println!(" ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ ");
|
||||||
println!(" ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ ");
|
println!(" ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ");
|
||||||
println!(" ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ ");
|
println!(" ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ ");
|
||||||
println!(" ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ");
|
println!(" ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ ");
|
||||||
println!(" ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ ");
|
println!(" ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ");
|
||||||
println!(" ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ");
|
println!(" ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ ");
|
||||||
println!(" ▒▒ ▒▒ ▒▒ ▒▒ ");
|
println!(" ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ");
|
||||||
println!();
|
println!(" ▒▒ ▒▒ ▒▒ ▒▒ ");
|
||||||
println!("We hope you enjoyed learning about the various aspects of Rust!");
|
println!();
|
||||||
println!(
|
println!("We hope you enjoyed learning about the various aspects of Rust!");
|
||||||
"If you noticed any issues, please don't hesitate to report them to our repo."
|
println!(
|
||||||
);
|
"If you noticed any issues, please don't hesitate to report them to our repo."
|
||||||
println!("You can also contribute your own exercises to help the greater community!");
|
);
|
||||||
println!();
|
println!(
|
||||||
println!("Before reporting an issue or contributing, please read our guidelines:");
|
"You can also contribute your own exercises to help the greater community!"
|
||||||
println!("https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md");
|
);
|
||||||
}
|
println!();
|
||||||
|
println!("Before reporting an issue or contributing, please read our guidelines:");
|
||||||
|
println!("https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md");
|
||||||
|
}
|
||||||
|
Ok(WatchStatus::Unfinished) => {
|
||||||
|
println!("We hope you're enjoying learning about Rust!");
|
||||||
|
println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again");
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>) {
|
fn spawn_watch_shell(
|
||||||
|
failed_exercise_hint: &Arc<Mutex<Option<String>>>,
|
||||||
|
should_quit: Arc<AtomicBool>,
|
||||||
|
) {
|
||||||
let failed_exercise_hint = Arc::clone(failed_exercise_hint);
|
let failed_exercise_hint = Arc::clone(failed_exercise_hint);
|
||||||
println!("Type 'hint' or open the corresponding README.md file to get help or type 'clear' to clear the screen.");
|
println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.");
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
match io::stdin().read_line(&mut input) {
|
match io::stdin().read_line(&mut input) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let input = input.trim();
|
let input = input.trim();
|
||||||
if input.eq("hint") {
|
if input == "hint" {
|
||||||
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
|
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
|
||||||
println!("{}", hint);
|
println!("{}", hint);
|
||||||
}
|
}
|
||||||
} else if input.eq("clear") {
|
} else if input == "clear" {
|
||||||
println!("\x1B[2J\x1B[1;1H");
|
println!("\x1B[2J\x1B[1;1H");
|
||||||
|
} else if input.eq("quit") {
|
||||||
|
should_quit.store(true, Ordering::SeqCst);
|
||||||
|
println!("Bye!");
|
||||||
|
} else if input.eq("help") {
|
||||||
|
println!("Commands available to you in watch mode:");
|
||||||
|
println!(" hint - prints the current exercise's hint");
|
||||||
|
println!(" clear - clears the screen");
|
||||||
|
println!(" quit - quits watch mode");
|
||||||
|
println!(" help - displays this help message");
|
||||||
|
println!();
|
||||||
|
println!("Watch mode automatically re-evaluates the current exercise");
|
||||||
|
println!("when you edit a file's contents.")
|
||||||
} else {
|
} else {
|
||||||
println!("unknown command: {}", input);
|
println!("unknown command: {}", input);
|
||||||
}
|
}
|
||||||
@ -306,7 +330,12 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
enum WatchStatus {
|
||||||
|
Finished,
|
||||||
|
Unfinished,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
|
||||||
/* Clears the terminal with an ANSI escape code.
|
/* Clears the terminal with an ANSI escape code.
|
||||||
Works in UNIX and newer Windows terminals. */
|
Works in UNIX and newer Windows terminals. */
|
||||||
fn clear_screen() {
|
fn clear_screen() {
|
||||||
@ -314,6 +343,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
let should_quit = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
|
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
|
||||||
watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;
|
watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;
|
||||||
@ -322,12 +352,12 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
|||||||
|
|
||||||
let to_owned_hint = |t: &Exercise| t.hint.to_owned();
|
let to_owned_hint = |t: &Exercise| t.hint.to_owned();
|
||||||
let failed_exercise_hint = match verify(exercises.iter(), verbose) {
|
let failed_exercise_hint = match verify(exercises.iter(), verbose) {
|
||||||
Ok(_) => return Ok(()),
|
Ok(_) => return Ok(WatchStatus::Finished),
|
||||||
Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))),
|
Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))),
|
||||||
};
|
};
|
||||||
spawn_watch_shell(&failed_exercise_hint);
|
spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit));
|
||||||
loop {
|
loop {
|
||||||
match rx.recv() {
|
match rx.recv_timeout(Duration::from_secs(1)) {
|
||||||
Ok(event) => match event {
|
Ok(event) => match event {
|
||||||
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
||||||
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
|
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
|
||||||
@ -343,7 +373,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
|||||||
);
|
);
|
||||||
clear_screen();
|
clear_screen();
|
||||||
match verify(pending_exercises, verbose) {
|
match verify(pending_exercises, verbose) {
|
||||||
Ok(_) => return Ok(()),
|
Ok(_) => return Ok(WatchStatus::Finished),
|
||||||
Err(exercise) => {
|
Err(exercise) => {
|
||||||
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
|
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
|
||||||
*failed_exercise_hint = Some(to_owned_hint(exercise));
|
*failed_exercise_hint = Some(to_owned_hint(exercise));
|
||||||
@ -353,8 +383,15 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
|||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
Err(RecvTimeoutError::Timeout) => {
|
||||||
|
// the timeout expired, just check the `should_quit` variable below then loop again
|
||||||
|
}
|
||||||
Err(e) => println!("watch error: {:?}", e),
|
Err(e) => println!("watch error: {:?}", e),
|
||||||
}
|
}
|
||||||
|
// Check if we need to exit
|
||||||
|
if should_quit.load(Ordering::SeqCst) {
|
||||||
|
return Ok(WatchStatus::Unfinished);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user