diff --git a/.all-contributorsrc b/.all-contributorsrc
index 85c66703..fe18385f 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -929,6 +929,97 @@
"contributions": [
"content"
]
+ },
+ {
+ "login": "dbednar230",
+ "name": "Damian",
+ "avatar_url": "https://avatars.githubusercontent.com/u/54457902?v=4",
+ "profile": "https://github.com/dbednar230",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "benarmstead",
+ "name": "Ben Armstead",
+ "avatar_url": "https://avatars.githubusercontent.com/u/70973680?v=4",
+ "profile": "https://benarmstead.co.uk",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "anuk909",
+ "name": "anuk909",
+ "avatar_url": "https://avatars.githubusercontent.com/u/34924662?v=4",
+ "profile": "https://github.com/anuk909",
+ "contributions": [
+ "content",
+ "code"
+ ]
+ },
+ {
+ "login": "granddaifuku",
+ "name": "granddaifuku",
+ "avatar_url": "https://avatars.githubusercontent.com/u/49578068?v=4",
+ "profile": "https://granddaifuku.com/",
+ "contributions": [
+ "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"
+ ]
}
],
"contributorsPerLine": 8,
diff --git a/.gitignore b/.gitignore
index 06de8710..253f8f33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ exercises/clippy/Cargo.toml
exercises/clippy/Cargo.lock
.idea
.vscode
+*.iml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67b8cdc3..fcc39e42 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,29 @@
+
+## 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))
+
+
+
## 4.5.0 (2021-07-07)
diff --git a/Cargo.lock b/Cargo.lock
index d291c169..e536d1b7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,19 +1,21 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
+version = 3
+
[[package]]
name = "aho-corasick"
-version = "0.7.15"
+version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "argh"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91792f088f87cdc7a2cfb1d617fa5ea18d7f1dc22ef0e1b5f82f3157cdc522be"
+checksum = "2e7317a549bc17c5278d9e72bb6e62c6aa801ac2567048e39ebc1c194249323e"
dependencies = [
"argh_derive",
"argh_shared",
@@ -21,9 +23,9 @@ dependencies = [
[[package]]
name = "argh_derive"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4eb0c0c120ad477412dc95a4ce31e38f2113e46bd13511253f79196ca68b067"
+checksum = "60949c42375351e9442e354434b0cba2ac402c1237edf673cac3a4bf983b8d3c"
dependencies = [
"argh_shared",
"heck",
@@ -34,9 +36,9 @@ dependencies = [
[[package]]
name = "argh_shared"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "781f336cc9826dbaddb9754cb5db61e64cab4f69668bd19dcc4a0394a86f4cb1"
+checksum = "8a61eb019cb8f415d162cb9f12130ee6bbe9168b7d953c17f4ad049e4051ca00"
[[package]]
name = "assert_cmd"
@@ -69,9 +71,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
@@ -156,9 +158,9 @@ dependencies = [
[[package]]
name = "filetime"
-version = "0.2.14"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
+checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
dependencies = [
"cfg-if 1.0.0",
"libc",
@@ -218,18 +220,18 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "heck"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
-version = "0.1.18"
+version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
@@ -269,9 +271,9 @@ dependencies = [
[[package]]
name = "instant"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
+checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
dependencies = [
"cfg-if 1.0.0",
]
@@ -287,9 +289,9 @@ dependencies = [
[[package]]
name = "itoa"
-version = "0.4.7"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "kernel32-sys"
@@ -315,15 +317,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
-version = "0.2.93"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
+checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]]
name = "lock_api"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
+checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
dependencies = [
"scopeguard",
]
@@ -339,9 +341,9 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.3.4"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "mio"
@@ -405,9 +407,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "notify"
-version = "4.0.16"
+version = "4.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2599080e87c9bd051ddb11b10074f4da7b1223298df65d4c2ec5bcf309af1533"
+checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257"
dependencies = [
"bitflags",
"filetime",
@@ -466,9 +468,9 @@ dependencies = [
[[package]]
name = "predicates"
-version = "1.0.7"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eeb433456c1a57cc93554dea3ce40b4c19c4057e41c55d4a0f3d84ea71c325aa"
+checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df"
dependencies = [
"difference",
"float-cmp",
@@ -485,9 +487,9 @@ checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
[[package]]
name = "predicates-tree"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2"
+checksum = "d7dd0fd014130206c9352efbdc92be592751b2b9274dff685348341082c6ea3d"
dependencies = [
"predicates-core",
"treeline",
@@ -495,9 +497,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.26"
+version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
+checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [
"unicode-xid",
]
@@ -513,18 +515,18 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.2.6"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
+checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
-version = "1.4.6"
+version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
@@ -533,13 +535,13 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.6.23"
+version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rustlings"
-version = "4.5.0"
+version = "4.6.0"
dependencies = [
"argh",
"assert_cmd",
@@ -576,18 +578,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
-version = "1.0.125"
+version = "1.0.129"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
+checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.125"
+version = "1.0.129"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
+checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3"
dependencies = [
"proc-macro2",
"quote",
@@ -596,9 +598,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.64"
+version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
+checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127"
dependencies = [
"itoa",
"ryu",
@@ -607,9 +609,9 @@ dependencies = [
[[package]]
name = "slab"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
+checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
[[package]]
name = "smallvec"
@@ -619,9 +621,9 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "syn"
-version = "1.0.70"
+version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883"
+checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
dependencies = [
"proc-macro2",
"quote",
@@ -630,9 +632,9 @@ dependencies = [
[[package]]
name = "terminal_size"
-version = "0.1.16"
+version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
+checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi 0.3.9",
@@ -664,9 +666,9 @@ checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
name = "unicode-segmentation"
-version = "1.7.1"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
+checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
@@ -676,9 +678,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
-version = "0.2.1"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "walkdir"
diff --git a/Cargo.toml b/Cargo.toml
index 615a09c3..3b2a85a7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rustlings"
-version = "4.5.0"
+version = "4.6.0"
authors = ["anastasie ", "Carol (Nichols || Goulding) "]
edition = "2018"
diff --git a/README.md b/README.md
index 7313c8e1..6e5ce3f6 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-[](#contributors-)
+[](#contributors-)
# 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`.
```bash
-# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 4.5.0)
-git clone -b 4.5.0 --depth 1 https://github.com/rust-lang/rustlings
+# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 4.6.0)
+git clone -b 4.6.0 --depth 1 https://github.com/rust-lang/rustlings
cd rustlings
cargo install --force --path .
```
@@ -302,6 +302,18 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 Ivan Nerazumov π |
 lauralindzey π |
 Rakshit Sinha π |
+  Damian π |
+  Ben Armstead π» |
+  anuk909 π π» |
+  granddaifuku π |
+
+
+  Weilet π |
+  LIU JIE π |
+  Antoine BΓΌsch π» |
+  frogtd π |
+  Zhenghao Lu π |
+  Fredrik Enestad π |
diff --git a/exercises/README.md b/exercises/README.md
index eebbd0bb..73754db5 100644
--- a/exercises/README.md
+++ b/exercises/README.md
@@ -9,7 +9,7 @@
| primitive_types | Β§4.3 |
| structs | Β§5.1 |
| enums | Β§6 |
-| modules | Β§7.2 |
+| modules | Β§7 |
| collections | Β§8.1, Β§8.3 |
| strings | Β§8.2 |
| error_handling | Β§9 |
diff --git a/exercises/advanced_errors/advanced_errs1.rs b/exercises/advanced_errors/advanced_errs1.rs
new file mode 100644
index 00000000..4bc7b635
--- /dev/null
+++ b/exercises/advanced_errors/advanced_errs1.rs
@@ -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 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 {
+ 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 {
+ 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()));
+ }
+}
diff --git a/exercises/advanced_errors/advanced_errs2.rs b/exercises/advanced_errors/advanced_errs2.rs
new file mode 100644
index 00000000..d9d44d06
--- /dev/null
+++ b/exercises/advanced_errors/advanced_errs2.rs
@@ -0,0 +1,203 @@
+// 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 for ParseClimateError {
+ fn from(e: ParseIntError) -> Self {
+ Self::ParseInt(e)
+ }
+}
+
+// This `From` implementation allows the `?` operator to work on
+// `ParseFloatError` values.
+impl From 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),
+ _ => write!(f, "unhandled error!"),
+ }
+ }
+}
+
+#[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 {
+ 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> {
+ println!("{:?}", "Hong Kong,1999,25.7".parse::()?);
+ println!("{:?}", "".parse::()?);
+ Ok(())
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ #[test]
+ fn test_empty() {
+ let res = "".parse::();
+ assert_eq!(res, Err(ParseClimateError::Empty));
+ assert_eq!(res.unwrap_err().to_string(), "empty input");
+ }
+ #[test]
+ fn test_short() {
+ let res = "Boston,1991".parse::();
+ 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::();
+ 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::();
+ 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::();
+ 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::();
+ 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::();
+ 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::();
+ 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::();
+ 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::());
+ }
+}
diff --git a/exercises/conversions/from_str.rs b/exercises/conversions/from_str.rs
index 93a5299a..ece0b3cf 100644
--- a/exercises/conversions/from_str.rs
+++ b/exercises/conversions/from_str.rs
@@ -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
// 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
-use std::error;
+use std::num::ParseIntError;
use std::str::FromStr;
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
struct Person {
name: String,
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::()
+ ParseInt(ParseIntError),
+}
+
// I AM NOT DONE
// Steps:
@@ -24,7 +39,7 @@ struct Person {
// If everything goes well, then return a Result of a Person object
impl FromStr for Person {
- type Err = Box;
+ type Err = ParsePersonError;
fn from_str(s: &str) -> Result {
}
}
@@ -40,7 +55,7 @@ mod tests {
#[test]
fn empty_input() {
- assert!("".parse::().is_err());
+ assert_eq!("".parse::(), Err(ParsePersonError::Empty));
}
#[test]
fn good_input() {
@@ -52,41 +67,56 @@ mod tests {
}
#[test]
fn missing_age() {
- assert!("John,".parse::().is_err());
+ assert!(matches!(
+ "John,".parse::(),
+ Err(ParsePersonError::ParseInt(_))
+ ));
}
#[test]
fn invalid_age() {
- assert!("John,twenty".parse::().is_err());
+ assert!(matches!(
+ "John,twenty".parse::(),
+ Err(ParsePersonError::ParseInt(_))
+ ));
}
#[test]
fn missing_comma_and_age() {
- assert!("John".parse::().is_err());
+ assert_eq!("John".parse::(), Err(ParsePersonError::BadLen));
}
#[test]
fn missing_name() {
- assert!(",1".parse::().is_err());
+ assert_eq!(",1".parse::(), Err(ParsePersonError::NoName));
}
#[test]
fn missing_name_and_age() {
- assert!(",".parse::().is_err());
+ assert!(matches!(
+ ",".parse::(),
+ Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
+ ));
}
#[test]
fn missing_name_and_invalid_age() {
- assert!(",one".parse::().is_err());
+ assert!(matches!(
+ ",one".parse::(),
+ Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
+ ));
}
#[test]
fn trailing_comma() {
- assert!("John,32,".parse::().is_err());
+ assert_eq!("John,32,".parse::(), Err(ParsePersonError::BadLen));
}
#[test]
fn trailing_comma_and_some_string() {
- assert!("John,32,man".parse::().is_err());
+ assert_eq!(
+ "John,32,man".parse::(),
+ Err(ParsePersonError::BadLen)
+ );
}
}
diff --git a/exercises/conversions/try_from_into.rs b/exercises/conversions/try_from_into.rs
index c0b5d986..b8ec4455 100644
--- a/exercises/conversions/try_from_into.rs
+++ b/exercises/conversions/try_from_into.rs
@@ -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.
// Basically, this is the same as From. The main difference is that this should return a Result type
// instead of the target type itself.
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
use std::convert::{TryFrom, TryInto};
-use std::error;
#[derive(Debug, PartialEq)]
struct Color {
@@ -12,12 +12,21 @@ struct Color {
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
// Your task is to complete this implementation
// and return an Ok result of inner type Color.
// 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,
// but the slice implementation needs to check the slice length!
@@ -25,20 +34,23 @@ struct Color {
// Tuple implementation
impl TryFrom<(i16, i16, i16)> for Color {
- type Error = Box;
- fn try_from(tuple: (i16, i16, i16)) -> Result {}
+ type Error = IntoColorError;
+ fn try_from(tuple: (i16, i16, i16)) -> Result {
+ }
}
// Array implementation
impl TryFrom<[i16; 3]> for Color {
- type Error = Box;
- fn try_from(arr: [i16; 3]) -> Result {}
+ type Error = IntoColorError;
+ fn try_from(arr: [i16; 3]) -> Result {
+ }
}
// Slice implementation
impl TryFrom<&[i16]> for Color {
- type Error = Box;
- fn try_from(slice: &[i16]) -> Result {}
+ type Error = IntoColorError;
+ fn try_from(slice: &[i16]) -> Result {
+ }
}
fn main() {
@@ -46,15 +58,15 @@ fn main() {
let c1 = Color::try_from((183, 65, 14));
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 = [183, 65, 14].try_into();
println!("{:?}", c2);
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[..]);
println!("{:?}", c3);
- // or take slice within round brackets and use Into
+ // or take slice within round brackets and use TryInto
let c4: Result = (&v[..]).try_into();
println!("{:?}", c4);
}
@@ -65,15 +77,24 @@ mod tests {
#[test]
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]
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]
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]
fn test_tuple_correct() {
@@ -91,17 +112,17 @@ mod tests {
#[test]
fn test_array_out_of_range_positive() {
let c: Result = [1000, 10000, 256].try_into();
- assert!(c.is_err());
+ assert_eq!(c, Err(IntoColorError::IntConversion));
}
#[test]
fn test_array_out_of_range_negative() {
let c: Result = [-10, -256, -1].try_into();
- assert!(c.is_err());
+ assert_eq!(c, Err(IntoColorError::IntConversion));
}
#[test]
fn test_array_sum() {
let c: Result = [-1, 255, 255].try_into();
- assert!(c.is_err());
+ assert_eq!(c, Err(IntoColorError::IntConversion));
}
#[test]
fn test_array_correct() {
@@ -119,17 +140,26 @@ mod tests {
#[test]
fn test_slice_out_of_range_positive() {
let arr = [10000, 256, 1000];
- assert!(Color::try_from(&arr[..]).is_err());
+ assert_eq!(
+ Color::try_from(&arr[..]),
+ Err(IntoColorError::IntConversion)
+ );
}
#[test]
fn test_slice_out_of_range_negative() {
let arr = [-256, -1, -10];
- assert!(Color::try_from(&arr[..]).is_err());
+ assert_eq!(
+ Color::try_from(&arr[..]),
+ Err(IntoColorError::IntConversion)
+ );
}
#[test]
fn test_slice_sum() {
let arr = [-1, 255, 255];
- assert!(Color::try_from(&arr[..]).is_err());
+ assert_eq!(
+ Color::try_from(&arr[..]),
+ Err(IntoColorError::IntConversion)
+ );
}
#[test]
fn test_slice_correct() {
@@ -148,11 +178,11 @@ mod tests {
#[test]
fn test_slice_excess_length() {
let v = vec![0, 0, 0, 0];
- assert!(Color::try_from(&v[..]).is_err());
+ assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen));
}
#[test]
fn test_slice_insufficient_length() {
let v = vec![0, 0];
- assert!(Color::try_from(&v[..]).is_err());
+ assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen));
}
}
diff --git a/exercises/modules/README.md b/exercises/modules/README.md
index 6582b000..3dc8a482 100644
--- a/exercises/modules/README.md
+++ b/exercises/modules/README.md
@@ -4,4 +4,4 @@ In this section we'll give you an introduction to Rust's module system.
## Further information
-- [The Module System](https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html)
+- [The Module System](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html)
diff --git a/exercises/modules/modules1.rs b/exercises/modules/modules1.rs
index 812dfeef..1a2bd0dd 100644
--- a/exercises/modules/modules1.rs
+++ b/exercises/modules/modules1.rs
@@ -4,7 +4,13 @@
// I AM NOT DONE
mod sausage_factory {
+ // Don't let anybody outside of this module see this!
+ fn get_secret_recipe() -> String {
+ String::from("Ginger")
+ }
+
fn make_sausage() {
+ get_secret_recipe();
println!("sausage!");
}
}
diff --git a/exercises/modules/modules2.rs b/exercises/modules/modules2.rs
index fde439d1..87f0c458 100644
--- a/exercises/modules/modules2.rs
+++ b/exercises/modules/modules2.rs
@@ -1,11 +1,15 @@
// modules2.rs
+// You can bring module paths into scopes and provide new names for them with the
+// 'use' and 'as' keywords. Fix these 'use' statements to make the code compile.
// Make me compile! Execute `rustlings hint modules2` for hints :)
// I AM NOT DONE
mod delicious_snacks {
- use self::fruits::PEAR as fruit;
- use self::veggies::CUCUMBER as veggie;
+
+ // TODO: Fix these use statements
+ use self::fruits::PEAR as ???
+ use self::veggies::CUCUMBER as ???
mod fruits {
pub const PEAR: &'static str = "Pear";
diff --git a/exercises/modules/modules3.rs b/exercises/modules/modules3.rs
new file mode 100644
index 00000000..8eed77df
--- /dev/null
+++ b/exercises/modules/modules3.rs
@@ -0,0 +1,18 @@
+// modules3.rs
+// You can use the 'use' keyword to bring module paths from modules from anywhere
+// and especially from the Rust standard library into your scope.
+// Bring SystemTime and UNIX_EPOCH
+// from the std::time module. Bonus style points if you can do it with one line!
+// Make me compile! Execute `rustlings hint modules3` for hints :)
+
+// I AM NOT DONE
+
+// TODO: Complete this use statement
+use ???
+
+fn main() {
+ match SystemTime::now().duration_since(UNIX_EPOCH) {
+ Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()),
+ Err(_) => panic!("SystemTime before UNIX EPOCH!"),
+ }
+}
diff --git a/exercises/move_semantics/move_semantics5.rs b/exercises/move_semantics/move_semantics5.rs
index 5449e951..c4704f9e 100644
--- a/exercises/move_semantics/move_semantics5.rs
+++ b/exercises/move_semantics/move_semantics5.rs
@@ -1,5 +1,5 @@
// 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.
// Execute `rustlings hint move_semantics5` for hints :)
@@ -8,7 +8,7 @@
fn main() {
let mut x = 100;
let y = &mut x;
- let z = &mut *y;
+ let z = &mut x;
*y += 100;
*z += 1000;
assert_eq!(x, 1200);
diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs
index 3af1293d..b13b9284 100644
--- a/exercises/quiz1.rs
+++ b/exercises/quiz1.rs
@@ -10,7 +10,7 @@
// I AM NOT DONE
// Put your function here!
-// fn ..... {
+// fn calculate_apple_price {
// Don't modify this function!
#[test]
diff --git a/exercises/structs/structs3.rs b/exercises/structs/structs3.rs
index a80d0625..b3aa2825 100644
--- a/exercises/structs/structs3.rs
+++ b/exercises/structs/structs3.rs
@@ -18,11 +18,11 @@ impl Package {
if weight_in_grams <= 0 {
// Something goes here...
} else {
- return Package {
+ Package {
sender_country,
recipient_country,
weight_in_grams,
- };
+ }
}
}
diff --git a/info.toml b/info.toml
index d5008ede..5eece6d7 100644
--- a/info.toml
+++ b/info.toml
@@ -362,11 +362,19 @@ name = "modules2"
path = "exercises/modules/modules2.rs"
mode = "compile"
hint = """
-The delicious_snacks module is trying to present an external
-interface (the `fruit` and `veggie` constants) that is different than
-its internal structure (the `fruits` and `veggies` modules and
-associated constants). It's almost there except for one keyword missing for
-each constant."""
+The delicious_snacks module is trying to present an external interface that is
+different than its internal structure (the `fruits` and `veggies` modules and
+associated constants). Complete the `use` statements to fit the uses in main and
+find the one keyword missing for both constants."""
+
+[[exercises]]
+name = "modules3"
+path = "exercises/modules/modules3.rs"
+mode = "compile"
+hint = """
+UNIX_EPOCH and SystemTime are declared in the std::time module. Add a use statement
+for these two to bring them into scope. You can use nested paths or the glob
+operator to bring these two in using only one line."""
# COLLECTIONS
@@ -933,6 +941,27 @@ mode = "test"
hint = """
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::`.
+
+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]]
name = "try_from_into"
path = "exercises/conversions/try_from_into.rs"
@@ -941,17 +970,19 @@ hint = """
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 might want to look back at the exercise errors5 (or its hints) to remind
-yourself about how `Box` works.
+Hint: Is there an implementation of `TryFrom` in the standard library that
+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`
-nor `String` implements `error::Error`. However, there is an implementation
-of `From<&str>` for `Box`. This means you can use `.into()` or
-the `?` operator to convert your string into the correct error type.
+Another hint: Look at the test cases to see which error variants to return.
-If you're having trouble with using the `?` operator to convert an error string,
-recall that `?` works to convert `Err(something)` into the appropriate error
-type for returning from the function."""
+Yet another hint: You can use the `map_err` or `or` methods of `Result` to
+convert errors.
+
+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]]
name = "as_ref_mut"
@@ -960,14 +991,54 @@ mode = "test"
hint = """
Add AsRef as a trait bound to the functions."""
+# ADVANCED ERRORS
+
[[exercises]]
-name = "from_str"
-path = "exercises/conversions/from_str.rs"
+name = "advanced_errs1"
+path = "exercises/advanced_errors/advanced_errs1.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 `try_from_into` exercise.
+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.
-If you're having trouble with returning the correct error type, see the
-hints for try_from_into."""
+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.
+"""
diff --git a/src/exercise.rs b/src/exercise.rs
index d934cfd3..ec694df9 100644
--- a/src/exercise.rs
+++ b/src/exercise.rs
@@ -133,7 +133,7 @@ path = "{}.rs""#,
"Failed to write π Clippy π Cargo.toml file."
};
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
// compilation failure, this would silently fail. But we expect
// clippy to reflect the same failure while compiling later.
@@ -162,7 +162,7 @@ path = "{}.rs""#,
if cmd.status.success() {
Ok(CompiledExercise {
- exercise: &self,
+ exercise: self,
_handle: FileHandle,
})
} else {
diff --git a/src/main.rs b/src/main.rs
index c8ebc68b..32e7bba2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,7 +10,8 @@ use std::fs;
use std::io::{self, prelude::*};
use std::path::Path;
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::thread;
use std::time::Duration;
@@ -23,7 +24,7 @@ mod run;
mod verify;
// In sync with crate version
-const VERSION: &str = "4.5.0";
+const VERSION: &str = "4.6.0";
#[derive(FromArgs, PartialEq, Debug)]
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
@@ -203,7 +204,7 @@ fn main() {
Subcommands::Run(subargs) => {
let exercise = find_exercise(&subargs.name, &exercises);
- run(&exercise, verbose).unwrap_or_else(|_| std::process::exit(1));
+ run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1));
}
Subcommands::Hint(subargs) => {
@@ -216,8 +217,8 @@ fn main() {
verify(&exercises, verbose).unwrap_or_else(|_| std::process::exit(1));
}
- Subcommands::Watch(_subargs) => {
- if let Err(e) = watch(&exercises, verbose) {
+ Subcommands::Watch(_subargs) => match watch(&exercises, verbose) {
+ Err(e) => {
println!(
"Error: Could not watch your progress. Error message was {:?}.",
e
@@ -225,57 +226,80 @@ fn main() {
println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
std::process::exit(1);
}
- println!(
- "{emoji} All exercises completed! {emoji}",
- emoji = Emoji("π", "β
")
- );
- 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!("We hope you enjoyed learning about the various aspects of Rust!");
- 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!("Before reporting an issue or contributing, please read our guidelines:");
- println!("https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md");
- }
+ Ok(WatchStatus::Finished) => {
+ println!(
+ "{emoji} All exercises completed! {emoji}",
+ emoji = Emoji("π", "β
")
+ );
+ 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!("We hope you enjoyed learning about the various aspects of Rust!");
+ 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!("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>>) {
+fn spawn_watch_shell(
+ failed_exercise_hint: &Arc>>,
+ should_quit: Arc,
+) {
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 {
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(_) => {
let input = input.trim();
- if input.eq("hint") {
+ if input == "hint" {
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
println!("{}", hint);
}
- } else if input.eq("clear") {
+ } else if input == "clear" {
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 {
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 {
/* Clears the terminal with an ANSI escape code.
Works in UNIX and newer Windows terminals. */
fn clear_screen() {
@@ -314,6 +343,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
}
let (tx, rx) = channel();
+ let should_quit = Arc::new(AtomicBool::new(false));
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
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 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)))),
};
- spawn_watch_shell(&failed_exercise_hint);
+ spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit));
loop {
- match rx.recv() {
+ match rx.recv_timeout(Duration::from_secs(1)) {
Ok(event) => match event {
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
@@ -343,7 +373,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
);
clear_screen();
match verify(pending_exercises, verbose) {
- Ok(_) => return Ok(()),
+ Ok(_) => return Ok(WatchStatus::Finished),
Err(exercise) => {
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
*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),
}
+ // Check if we need to exit
+ if should_quit.load(Ordering::SeqCst) {
+ return Ok(WatchStatus::Unfinished);
+ }
}
}
diff --git a/src/verify.rs b/src/verify.rs
index b98598a8..fd59fa51 100644
--- a/src/verify.rs
+++ b/src/verify.rs
@@ -14,9 +14,9 @@ pub fn verify<'a>(
) -> Result<(), &'a Exercise> {
for exercise in start_at {
let compile_result = match exercise.mode {
- Mode::Test => compile_and_test(&exercise, RunMode::Interactive, verbose),
- Mode::Compile => compile_and_run_interactively(&exercise),
- Mode::Clippy => compile_only(&exercise),
+ Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose),
+ Mode::Compile => compile_and_run_interactively(exercise),
+ Mode::Clippy => compile_only(exercise),
};
if !compile_result.unwrap_or(false) {
return Err(exercise);
@@ -42,11 +42,11 @@ fn compile_only(exercise: &Exercise) -> Result {
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
progress_bar.enable_steady_tick(100);
- let _ = compile(&exercise, &progress_bar)?;
+ let _ = compile(exercise, &progress_bar)?;
progress_bar.finish_and_clear();
success!("Successfully compiled {}!", exercise);
- Ok(prompt_for_completion(&exercise, None))
+ Ok(prompt_for_completion(exercise, None))
}
// Compile the given Exercise and run the resulting binary in an interactive mode
@@ -55,7 +55,7 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result {
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
progress_bar.enable_steady_tick(100);
- let compilation = compile(&exercise, &progress_bar)?;
+ let compilation = compile(exercise, &progress_bar)?;
progress_bar.set_message(format!("Running {}...", exercise).as_str());
let result = compilation.run();
@@ -73,7 +73,7 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result {
success!("Successfully ran {}!", exercise);
- Ok(prompt_for_completion(&exercise, Some(output.stdout)))
+ Ok(prompt_for_completion(exercise, Some(output.stdout)))
}
// Compile the given Exercise as a test harness and display
@@ -94,7 +94,7 @@ fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Re
}
success!("Successfully tested {}", &exercise);
if let RunMode::Interactive = run_mode {
- Ok(prompt_for_completion(&exercise, None))
+ Ok(prompt_for_completion(exercise, None))
} else {
Ok(true)
}