Merge branch 'main' into text

This commit is contained in:
apogeeoak 2022-02-04 19:27:42 -05:00
commit c1f35e46df
43 changed files with 1380 additions and 351 deletions

View file

@ -847,6 +847,251 @@
"contributions": [ "contributions": [
"content" "content"
] ]
},
{
"login": "PiDelport",
"name": "Pi Delport",
"avatar_url": "https://avatars.githubusercontent.com/u/630271?v=4",
"profile": "https://about.me/pjdelport",
"contributions": [
"content"
]
},
{
"login": "sateeshkumarb",
"name": "Sateesh ",
"avatar_url": "https://avatars.githubusercontent.com/u/429263?v=4",
"profile": "https://github.com/sateeshkumarb",
"contributions": [
"code",
"content"
]
},
{
"login": "kayuapi",
"name": "ZC",
"avatar_url": "https://avatars.githubusercontent.com/u/10304328?v=4",
"profile": "https://github.com/kayuapi",
"contributions": [
"content"
]
},
{
"login": "hyperparabolic",
"name": "hyperparabolic",
"avatar_url": "https://avatars.githubusercontent.com/u/12348474?v=4",
"profile": "https://github.com/hyperparabolic",
"contributions": [
"code"
]
},
{
"login": "kolbma",
"name": "arlecchino",
"avatar_url": "https://avatars.githubusercontent.com/u/5228369?v=4",
"profile": "https://www.net4visions.at",
"contributions": [
"doc"
]
},
{
"login": "jazzplato",
"name": "Richthofen",
"avatar_url": "https://avatars.githubusercontent.com/u/7576730?v=4",
"profile": "https://richthofen.io/",
"contributions": [
"code"
]
},
{
"login": "cseltol",
"name": "Ivan Nerazumov",
"avatar_url": "https://avatars.githubusercontent.com/u/64264529?v=4",
"profile": "https://github.com/cseltol",
"contributions": [
"doc"
]
},
{
"login": "lauralindzey",
"name": "lauralindzey",
"avatar_url": "https://avatars.githubusercontent.com/u/65185744?v=4",
"profile": "https://github.com/lauralindzey",
"contributions": [
"doc"
]
},
{
"login": "sinharaksh1t",
"name": "Rakshit Sinha",
"avatar_url": "https://avatars.githubusercontent.com/u/28585848?v=4",
"profile": "https://github.com/sinharaksh1t",
"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"
]
},
{
"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"
]
},
{
"login": "alirezaghey",
"name": "alirezaghey",
"avatar_url": "https://avatars.githubusercontent.com/u/26653424?v=4",
"profile": "https://github.com/alirezaghey",
"contributions": [
"content"
]
},
{
"login": "frvannes16",
"name": "Franklin van Nes",
"avatar_url": "https://avatars.githubusercontent.com/u/3188475?v=4",
"profile": "https://github.com/frvannes16",
"contributions": [
"code"
]
},
{
"login": "nekonako",
"name": "nekonako",
"avatar_url": "https://avatars.githubusercontent.com/u/46141275?v=4",
"profile": "https://nekonako.github.io",
"contributions": [
"code"
]
},
{
"login": "tan-zx",
"name": "ZX",
"avatar_url": "https://avatars.githubusercontent.com/u/67887489?v=4",
"profile": "https://github.com/tan-zx",
"contributions": [
"content"
]
},
{
"login": "sundevilyang",
"name": "Yang Wen",
"avatar_url": "https://avatars.githubusercontent.com/u/1499214?v=4",
"profile": "https://github.com/sundevilyang",
"contributions": [
"content"
]
},
{
"login": "highb",
"name": "Brandon High",
"avatar_url": "https://avatars.githubusercontent.com/u/759848?v=4",
"profile": "https://brandon-high.com",
"contributions": [
"doc"
]
} }
], ],
"contributorsPerLine": 8, "contributorsPerLine": 8,

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ exercises/clippy/Cargo.toml
exercises/clippy/Cargo.lock exercises/clippy/Cargo.lock
.idea .idea
.vscode .vscode
*.iml

View file

@ -1,3 +1,53 @@
<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>
## 4.5.0 (2021-07-07)
#### Features
* Add move_semantics5 exercise. (#746) ([399ab328](https://github.com/rust-lang/rustlings/commit/399ab328d8d407265c09563aa4ef4534b2503ff2))
* **cli:** Add "next" to run the next unsolved exercise. (#785) ([d20e413a](https://github.com/rust-lang/rustlings/commit/d20e413a68772cd493561f2651cf244e822b7ca5))
#### Bug Fixes
* rename result1 to errors4 ([50ab289d](https://github.com/rust-lang/rustlings/commit/50ab289da6b9eb19a7486c341b00048c516b88c0))
* move_semantics5 hints ([1b858285](https://github.com/rust-lang/rustlings/commit/1b85828548f46f58b622b5e0c00f8c989f928807))
* remove trailing whitespaces from iterators1 ([4d4fa774](https://github.com/rust-lang/rustlings/commit/4d4fa77459392acd3581c6068aa8be9a02de12fc))
* add hints to generics1 and generics2 exercises ([31457940](https://github.com/rust-lang/rustlings/commit/31457940846b3844d78d4a4d2b074bc8d6aaf1eb))
* remove trailing whitespace ([d9b69bd1](https://github.com/rust-lang/rustlings/commit/d9b69bd1a0a7a99f2c0d80933ad2eea44c8c71b2))
* **installation:** first PowerShell command ([aa9a943d](https://github.com/rust-lang/rustlings/commit/aa9a943ddf3ae260782e73c26bcc9db60e5894b6))
* **iterators5:** derive Clone, Copy ([91fc9e31](https://github.com/rust-lang/rustlings/commit/91fc9e3118f4af603c9911698cc2a234725cb032))
* **quiz1:** Updated question description (#794) ([d8766496](https://github.com/rust-lang/rustlings/commit/d876649616cc8a8dd5f539f8bc1a5434b960b1e9))
* **try_from_into, from_str:** hints for dyn Error ([11d2cf0d](https://github.com/rust-lang/rustlings/commit/11d2cf0d604dee3f5023c17802d69438e69fa50e))
* **variables5:** confine the answer further ([48ffcbd2](https://github.com/rust-lang/rustlings/commit/48ffcbd2c4cc4d936c2c7480019190f179813cc5))
<a name="4.4.0"></a> <a name="4.4.0"></a>
## 4.4.0 (2021-04-24) ## 4.4.0 (2021-04-24)

116
Cargo.lock generated
View file

@ -1,19 +1,21 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.15" version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]] [[package]]
name = "argh" name = "argh"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91792f088f87cdc7a2cfb1d617fa5ea18d7f1dc22ef0e1b5f82f3157cdc522be" checksum = "2e7317a549bc17c5278d9e72bb6e62c6aa801ac2567048e39ebc1c194249323e"
dependencies = [ dependencies = [
"argh_derive", "argh_derive",
"argh_shared", "argh_shared",
@ -21,9 +23,9 @@ dependencies = [
[[package]] [[package]]
name = "argh_derive" name = "argh_derive"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4eb0c0c120ad477412dc95a4ce31e38f2113e46bd13511253f79196ca68b067" checksum = "60949c42375351e9442e354434b0cba2ac402c1237edf673cac3a4bf983b8d3c"
dependencies = [ dependencies = [
"argh_shared", "argh_shared",
"heck", "heck",
@ -34,9 +36,9 @@ dependencies = [
[[package]] [[package]]
name = "argh_shared" name = "argh_shared"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "781f336cc9826dbaddb9754cb5db61e64cab4f69668bd19dcc4a0394a86f4cb1" checksum = "8a61eb019cb8f415d162cb9f12130ee6bbe9168b7d953c17f4ad049e4051ca00"
[[package]] [[package]]
name = "assert_cmd" name = "assert_cmd"
@ -69,9 +71,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -156,9 +158,9 @@ dependencies = [
[[package]] [[package]]
name = "filetime" name = "filetime"
version = "0.2.14" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
@ -218,18 +220,18 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.2" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.18" version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -269,9 +271,9 @@ dependencies = [
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.9" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
@ -287,9 +289,9 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]] [[package]]
name = "kernel32-sys" name = "kernel32-sys"
@ -315,15 +317,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.93" version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.3" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
dependencies = [ dependencies = [
"scopeguard", "scopeguard",
] ]
@ -339,9 +341,9 @@ dependencies = [
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.3.4" version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]] [[package]]
name = "mio" name = "mio"
@ -405,9 +407,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]] [[package]]
name = "notify" name = "notify"
version = "4.0.16" version = "4.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2599080e87c9bd051ddb11b10074f4da7b1223298df65d4c2ec5bcf309af1533" checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"filetime", "filetime",
@ -466,9 +468,9 @@ dependencies = [
[[package]] [[package]]
name = "predicates" name = "predicates"
version = "1.0.7" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eeb433456c1a57cc93554dea3ce40b4c19c4057e41c55d4a0f3d84ea71c325aa" checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df"
dependencies = [ dependencies = [
"difference", "difference",
"float-cmp", "float-cmp",
@ -485,9 +487,9 @@ checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
[[package]] [[package]]
name = "predicates-tree" name = "predicates-tree"
version = "1.0.2" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2" checksum = "d7dd0fd014130206c9352efbdc92be592751b2b9274dff685348341082c6ea3d"
dependencies = [ dependencies = [
"predicates-core", "predicates-core",
"treeline", "treeline",
@ -495,9 +497,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.26" version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
@ -513,18 +515,18 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.6" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.4.6" version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -533,13 +535,13 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.23" version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]] [[package]]
name = "rustlings" name = "rustlings"
version = "4.4.0" version = "4.6.0"
dependencies = [ dependencies = [
"argh", "argh",
"assert_cmd", "assert_cmd",
@ -576,18 +578,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.125" version = "1.0.129"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.125" version = "1.0.129"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -596,9 +598,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.64" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -607,9 +609,9 @@ dependencies = [
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.3" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
@ -619,9 +621,9 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.70" version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883" checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -630,9 +632,9 @@ dependencies = [
[[package]] [[package]]
name = "terminal_size" name = "terminal_size"
version = "0.1.16" version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [ dependencies = [
"libc", "libc",
"winapi 0.3.9", "winapi 0.3.9",
@ -664,9 +666,9 @@ checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.7.1" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
@ -676,9 +678,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]] [[package]]
name = "walkdir" name = "walkdir"

View file

@ -1,8 +1,8 @@
[package] [package]
name = "rustlings" name = "rustlings"
version = "4.4.0" version = "4.6.0"
authors = ["Marisa <mokou@posteo.de>", "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"

View file

@ -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 -->
[![All Contributors](https://img.shields.io/badge/all_contributors-91-orange.svg?style=flat-square)](#contributors-) [![All Contributors](https://img.shields.io/badge/all_contributors-118-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END --> <!-- ALL-CONTRIBUTORS-BADGE:END -->
# rustlings 🦀❤️ # rustlings 🦀❤️
@ -16,6 +16,7 @@ Alternatively, for a first-time Rust learner, there are several other resources:
## Getting Started ## Getting Started
_Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._ _Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._
_Note: If you're on Linux, make sure you've installed gcc. Deb: `sudo apt install gcc`. Yum: `sudo yum -y install gcc`._
You will need to have Rust installed. You can get it by visiting https://rustup.rs. This'll also install Cargo, Rust's package/project manager. You will need to have Rust installed. You can get it by visiting https://rustup.rs. This'll also install Cargo, Rust's package/project manager.
@ -36,7 +37,7 @@ This will install Rustlings and give you access to the `rustlings` command. Run
In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`: In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`:
```ps ```ps
Set-ExecutionPolicy RemoteSigned Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
``` ```
Then, you can run: Then, you can run:
@ -57,12 +58,12 @@ When you get a permission denied message then you have to exclude the directory
## Manually ## Manually
Basically: Clone the repository, checkout to the latest tag, run `cargo install`. Basically: Clone the repository at the latest tag, run `cargo install`.
```bash ```bash
git clone 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 cd rustlings
git checkout tags/4.4.0 # or whatever the latest version is (find out at https://github.com/rust-lang/rustlings/releases/latest)
cargo install --force --path . cargo install --force --path .
``` ```
@ -97,6 +98,12 @@ In case you want to go by your own order, or want to only verify a single exerci
rustlings run myExercise1 rustlings run myExercise1
``` ```
Or simply use the following command to run the next unsolved exercise in the course:
```bash
rustlings run next
```
In case you get stuck, you can run the following command to get a hint for your In case you get stuck, you can run the following command to get a hint for your
exercise: exercise:
@ -104,6 +111,12 @@ exercise:
rustlings hint myExercise1 rustlings hint myExercise1
``` ```
You can also get the hint for the next unsolved exercise with the following command:
``` bash
rustlings hint next
```
To check your progress, you can run the following command: To check your progress, you can run the following command:
```bash ```bash
rustlings list rustlings list
@ -279,6 +292,39 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="https://github.com/hongshaoyang"><img src="https://avatars.githubusercontent.com/u/19281800?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shao Yang Hong</b></sub></a><br /><a href="#content-hongshaoyang" title="Content">🖋</a></td> <td align="center"><a href="https://github.com/hongshaoyang"><img src="https://avatars.githubusercontent.com/u/19281800?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shao Yang Hong</b></sub></a><br /><a href="#content-hongshaoyang" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/bmacer"><img src="https://avatars.githubusercontent.com/u/13931806?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brandon Macer</b></sub></a><br /><a href="#content-bmacer" title="Content">🖋</a></td> <td align="center"><a href="https://github.com/bmacer"><img src="https://avatars.githubusercontent.com/u/13931806?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brandon Macer</b></sub></a><br /><a href="#content-bmacer" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/stoiandan"><img src="https://avatars.githubusercontent.com/u/10388612?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stoian Dan</b></sub></a><br /><a href="#content-stoiandan" title="Content">🖋</a></td> <td align="center"><a href="https://github.com/stoiandan"><img src="https://avatars.githubusercontent.com/u/10388612?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stoian Dan</b></sub></a><br /><a href="#content-stoiandan" title="Content">🖋</a></td>
<td align="center"><a href="https://about.me/pjdelport"><img src="https://avatars.githubusercontent.com/u/630271?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pi Delport</b></sub></a><br /><a href="#content-PiDelport" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/sateeshkumarb"><img src="https://avatars.githubusercontent.com/u/429263?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sateesh </b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=sateeshkumarb" title="Code">💻</a> <a href="#content-sateeshkumarb" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/kayuapi"><img src="https://avatars.githubusercontent.com/u/10304328?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ZC</b></sub></a><br /><a href="#content-kayuapi" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/hyperparabolic"><img src="https://avatars.githubusercontent.com/u/12348474?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hyperparabolic</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=hyperparabolic" title="Code">💻</a></td>
<td align="center"><a href="https://www.net4visions.at"><img src="https://avatars.githubusercontent.com/u/5228369?v=4?s=100" width="100px;" alt=""/><br /><sub><b>arlecchino</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=kolbma" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://richthofen.io/"><img src="https://avatars.githubusercontent.com/u/7576730?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richthofen</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=jazzplato" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/cseltol"><img src="https://avatars.githubusercontent.com/u/64264529?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ivan Nerazumov</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=cseltol" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/lauralindzey"><img src="https://avatars.githubusercontent.com/u/65185744?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lauralindzey</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=lauralindzey" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/sinharaksh1t"><img src="https://avatars.githubusercontent.com/u/28585848?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rakshit Sinha</b></sub></a><br /><a href="#content-sinharaksh1t" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/dbednar230"><img src="https://avatars.githubusercontent.com/u/54457902?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Damian</b></sub></a><br /><a href="#content-dbednar230" title="Content">🖋</a></td>
<td align="center"><a href="https://benarmstead.co.uk"><img src="https://avatars.githubusercontent.com/u/70973680?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ben Armstead</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=benarmstead" 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>
</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>
<tr>
<td align="center"><a href="https://github.com/alirezaghey"><img src="https://avatars.githubusercontent.com/u/26653424?v=4?s=100" width="100px;" alt=""/><br /><sub><b>alirezaghey</b></sub></a><br /><a href="#content-alirezaghey" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/frvannes16"><img src="https://avatars.githubusercontent.com/u/3188475?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Franklin van Nes</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=frvannes16" title="Code">💻</a></td>
<td align="center"><a href="https://nekonako.github.io"><img src="https://avatars.githubusercontent.com/u/46141275?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nekonako</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=nekonako" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tan-zx"><img src="https://avatars.githubusercontent.com/u/67887489?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ZX</b></sub></a><br /><a href="#content-tan-zx" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/sundevilyang"><img src="https://avatars.githubusercontent.com/u/1499214?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yang Wen</b></sub></a><br /><a href="#content-sundevilyang" title="Content">🖋</a></td>
<td align="center"><a href="https://brandon-high.com"><img src="https://avatars.githubusercontent.com/u/759848?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brandon High</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=highb" title="Documentation">📖</a></td>
</tr> </tr>
</table> </table>

View file

@ -9,8 +9,8 @@
| primitive_types | §4.3 | | primitive_types | §4.3 |
| structs | §5.1 | | structs | §5.1 |
| enums | §6 | | enums | §6 |
| modules | §7.2 | | modules | §7 |
| collections | §8.1 | | collections | §8.1, §8.3 |
| strings | §8.2 | | strings | §8.2 |
| error_handling | §9 | | error_handling | §9 |
| generics | §10 | | generics | §10 |

View 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()));
}
}

View 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>());
}
}

View file

@ -8,10 +8,16 @@
// I AM NOT DONE // I AM NOT DONE
use std::f32;
fn main() { fn main() {
let x = 1.2331f64; let pi = 3.14f32;
let y = 1.2332f64; let radius = 5.00f32;
if y != x {
println!("Success!"); let area = pi * f32::powi(radius, 2);
}
println!(
"The area of a circle with radius {:.2} is {:.5}!",
radius, area
)
} }

View file

@ -20,3 +20,4 @@ structures that are used very often in Rust programs:
## Further information ## Further information
- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) - [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html)
- [Storing Keys with Associated Values in Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html)

View file

@ -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:
@ -20,11 +35,11 @@ struct Person {
// 4. Extract the first element from the split operation and use it as the name // 4. Extract the first element from the split operation and use it as the name
// 5. Extract the other element from the split operation and parse it into a `usize` as the age // 5. Extract the other element from the split operation and parse it into a `usize` as the age
// with something like `"4".parse::<usize>()` // with something like `"4".parse::<usize>()`
// 5. If while extracting the name and the age something goes wrong, an error should be returned // 6. If while extracting the name and the age something goes wrong, an error should be returned
// 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)
);
} }
} }

View file

@ -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));
} }
} }

View file

@ -36,6 +36,7 @@ mod tests {
fn explains_why_generating_nametag_text_fails() { fn explains_why_generating_nametag_text_fails() {
assert_eq!( assert_eq!(
generate_nametag_text("".into()), generate_nametag_text("".into()),
// Don't change this line
Err("`name` was empty; it must be nonempty.".into()) Err("`name` was empty; it must be nonempty.".into())
); );
} }

View file

@ -1,5 +1,5 @@
// result1.rs // errors4.rs
// Make this test pass! Execute `rustlings hint result1` for hints :) // Make this test pass! Execute `rustlings hint errors4` for hints :)
// I AM NOT DONE // I AM NOT DONE

View file

@ -0,0 +1,53 @@
// errors5.rs
// This program uses a completed version of the code from errors4.
// It won't compile right now! Why?
// Execute `rustlings hint errors5` for hints!
// I AM NOT DONE
use std::error;
use std::fmt;
use std::num::ParseIntError;
// TODO: update the return type of `main()` to make this compile.
fn main() -> Result<(), ParseIntError> {
let pretend_user_input = "42";
let x: i64 = pretend_user_input.parse()?;
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
Ok(())
}
// Don't change anything below this line.
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
x if x == 0 => Err(CreationError::Zero),
x => Ok(PositiveNonzeroInteger(x as u64))
}
}
}
// This is required so that `CreationError` can implement `error::Error`.
impl fmt::Display for CreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self {
CreationError::Negative => "number is negative",
CreationError::Zero => "number is zero",
};
f.write_str(description)
}
}
impl error::Error for CreationError {}

View file

@ -0,0 +1,95 @@
// errors6.rs
// Using catch-all error types like `Box<dyn error::Error>` isn't recommended
// for library code, where callers might want to make decisions based on the
// error content, instead of printing it out or propagating it further. Here,
// we define a custom error type to make it possible for callers to decide
// what to do next when our function returns an error.
// Make these tests pass! Execute `rustlings hint errors6` for hints :)
// I AM NOT DONE
use std::num::ParseIntError;
// This is a custom error type that we will be using in `parse_pos_nonzero()`.
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
Creation(CreationError),
ParseInt(ParseIntError)
}
impl ParsePosNonzeroError {
fn from_creation(err: CreationError) -> ParsePosNonzeroError {
ParsePosNonzeroError::Creation(err)
}
// TODO: add another error conversion function here.
}
fn parse_pos_nonzero(s: &str)
-> Result<PositiveNonzeroInteger, ParsePosNonzeroError>
{
// TODO: change this to return an appropriate error instead of panicking
// when `parse()` returns an error.
let x: i64 = s.parse().unwrap();
PositiveNonzeroInteger::new(x)
.map_err(ParsePosNonzeroError::from_creation)
}
// Don't change anything below this line.
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
x if x == 0 => Err(CreationError::Zero),
x => Ok(PositiveNonzeroInteger(x as u64))
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_error() {
// We can't construct a ParseIntError, so we have to pattern match.
assert!(matches!(
parse_pos_nonzero("not a number"),
Err(ParsePosNonzeroError::ParseInt(_))
));
}
#[test]
fn test_negative() {
assert_eq!(
parse_pos_nonzero("-555"),
Err(ParsePosNonzeroError::Creation(CreationError::Negative))
);
}
#[test]
fn test_zero() {
assert_eq!(
parse_pos_nonzero("0"),
Err(ParsePosNonzeroError::Creation(CreationError::Zero))
);
}
#[test]
fn test_positive() {
let x = PositiveNonzeroInteger::new(42);
assert!(x.is_ok());
assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap()));
}
}

View file

@ -1,117 +0,0 @@
// errorsn.rs
// This is a bigger error exercise than the previous ones!
// You can do it! :)
//
// Edit the `read_and_validate` function ONLY. Don't create any Errors
// that do not already exist.
//
// So many things could go wrong!
//
// - Reading from stdin could produce an io::Error
// - Parsing the input could produce a num::ParseIntError
// - Validating the input could produce a CreationError (defined below)
//
// How can we lump these errors into one general error? That is, what
// type goes where the question marks are, and how do we return
// that type from the body of read_and_validate?
//
// Execute `rustlings hint errorsn` for hints :)
// I AM NOT DONE
use std::error;
use std::fmt;
use std::io;
// PositiveNonzeroInteger is a struct defined below the tests.
fn read_and_validate(b: &mut dyn io::BufRead) -> Result<PositiveNonzeroInteger, ???> {
let mut line = String::new();
b.read_line(&mut line);
let num: i64 = line.trim().parse();
let answer = PositiveNonzeroInteger::new(num);
answer
}
//
// Nothing below this needs to be modified
//
// This is a test helper function that turns a &str into a BufReader.
fn test_with_str(s: &str) -> Result<PositiveNonzeroInteger, Box<dyn error::Error>> {
let mut b = io::BufReader::new(s.as_bytes());
read_and_validate(&mut b)
}
#[test]
fn test_success() {
let x = test_with_str("42\n");
assert_eq!(PositiveNonzeroInteger(42), x.unwrap());
}
#[test]
fn test_not_num() {
let x = test_with_str("eleven billion\n");
assert!(x.is_err());
}
#[test]
fn test_non_positive() {
let x = test_with_str("-40\n");
assert!(x.is_err());
}
#[test]
fn test_ioerror() {
struct Broken;
impl io::Read for Broken {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!"))
}
}
let mut b = io::BufReader::new(Broken);
assert!(read_and_validate(&mut b).is_err());
assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string());
}
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
if value == 0 {
Err(CreationError::Zero)
} else if value < 0 {
Err(CreationError::Negative)
} else {
Ok(PositiveNonzeroInteger(value as u64))
}
}
}
#[test]
fn test_positive_nonzero_integer_creation() {
assert!(PositiveNonzeroInteger::new(10).is_ok());
assert_eq!(
Err(CreationError::Negative),
PositiveNonzeroInteger::new(-10)
);
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
}
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
impl fmt::Display for CreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self {
CreationError::Negative => "Number is negative",
CreationError::Zero => "Number is zero",
};
f.write_str(description)
}
}
impl error::Error for CreationError {}

View file

@ -1,6 +1,8 @@
// This shopping list program isn't compiling! // This shopping list program isn't compiling!
// Use your knowledge of generics to fix it. // Use your knowledge of generics to fix it.
// Execute `rustlings hint generics1` for hints!
// I AM NOT DONE // I AM NOT DONE
fn main() { fn main() {

View file

@ -1,6 +1,8 @@
// This powerful wrapper provides the ability to store a positive integer value. // This powerful wrapper provides the ability to store a positive integer value.
// Rewrite it using generics so that it supports wrapping ANY type. // Rewrite it using generics so that it supports wrapping ANY type.
// Execute `rustlings hint generics2` for hints!
// I AM NOT DONE // I AM NOT DONE
struct Wrapper { struct Wrapper {

View file

@ -0,0 +1,8 @@
# Intro
Rust uses the `print!` and `println!` macros to print text to the console.
## Further information
- [Hello World](https://doc.rust-lang.org/rust-by-example/hello.html)
- [Formatted print](https://doc.rust-lang.org/rust-by-example/hello/print.html)

23
exercises/intro/intro1.rs Normal file
View file

@ -0,0 +1,23 @@
// intro1.rs
// About this `I AM NOT DONE` thing:
// We sometimes encourage you to keep trying things on a given exercise, even
// after you already figured it out. If you got everything working and feel
// ready for the next exercise, remove the `I AM NOT DONE` comment below.
// Execute the command `rustlings hint intro1` for a hint.
// I AM NOT DONE
fn main() {
println!("Hello and");
println!(r#" welcome to... "#);
println!(r#" _ _ _ "#);
println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);
println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#);
println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
println!(r#" |___/ "#);
println!();
println!("This exercise compiles successfully. The remaining exercises contain a compiler");
println!("or logic error. The central concept behind Rustlings is to fix these errors and");
println!("solve the exercises. Good luck!");
}

View file

@ -0,0 +1,9 @@
// intro2.rs
// Make the code print a greeting to the world.
// Execute `rustlings hint intro2` for a hint.
// I AM NOT DONE
fn main() {
println!("Hello {}!");
}

View file

@ -4,4 +4,4 @@ In this section we'll give you an introduction to Rust's module system.
## Further information ## 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)

View file

@ -4,7 +4,13 @@
// I AM NOT DONE // I AM NOT DONE
mod sausage_factory { mod sausage_factory {
// Don't let anybody outside of this module see this!
fn get_secret_recipe() -> String {
String::from("Ginger")
}
fn make_sausage() { fn make_sausage() {
get_secret_recipe();
println!("sausage!"); println!("sausage!");
} }
} }

View file

@ -1,11 +1,15 @@
// modules2.rs // 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 :) // Make me compile! Execute `rustlings hint modules2` for hints :)
// I AM NOT DONE // I AM NOT DONE
mod delicious_snacks { 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 { mod fruits {
pub const PEAR: &'static str = "Pear"; pub const PEAR: &'static str = "Pear";

View file

@ -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!"),
}
}

View file

@ -0,0 +1,15 @@
// move_semantics5.rs
// 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 :)
// I AM NOT DONE
fn main() {
let mut x = 100;
let y = &mut x;
let z = &mut x;
*y += 100;
*z += 1000;
assert_eq!(x, 1200);
}

View file

@ -16,3 +16,5 @@ Option types are very common in Rust code, as they have a number of uses:
- [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions) - [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions)
- [Option Module Documentation](https://doc.rust-lang.org/std/option/) - [Option Module Documentation](https://doc.rust-lang.org/std/option/)
- [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html) - [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html)
- [if let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html)
- [while let](https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html)

View file

@ -3,7 +3,7 @@
// I AM NOT DONE // I AM NOT DONE
// you can modify anything EXCEPT for this function's sig // you can modify anything EXCEPT for this function's signature
fn print_number(maybe_number: Option<u16>) { fn print_number(maybe_number: Option<u16>) {
println!("printing: {}", maybe_number.unwrap()); println!("printing: {}", maybe_number.unwrap());
} }

View file

@ -0,0 +1,19 @@
// option3.rs
// Make me compile! Execute `rustlings hint option3` for hints
// I AM NOT DONE
struct Point {
x: i32,
y: i32,
}
fn main() {
let y: Option<Point> = Some(Point { x: 100, y: 200 });
match y {
Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y),
_ => println!("no match"),
}
y; // Fix without deleting this line.
}

View file

@ -2,15 +2,16 @@
// This is a quiz for the following sections: // This is a quiz for the following sections:
// - Variables // - Variables
// - Functions // - Functions
// - If
// Mary is buying apples. One apple usually costs 2 Rustbucks, but if you buy // Mary is buying apples. One apple usually costs 2 Rustbucks, but if you buy
// more than 40 at once, each apple only costs 1! Write a function that calculates // more than 40 at once, each apple only costs 1! Write a function that calculates
// the price of an order of apples given the order amount. No hints this time! // the price of an order of apples given the quantity bought. No hints this time!
// I AM NOT DONE // I AM NOT DONE
// Put your function here! // Put your function here!
// fn ..... { // fn calculate_apple_price {
// Don't modify this function! // Don't modify this function!
#[test] #[test]

View file

@ -1,5 +1,4 @@
// iterators5.rs // iterators5.rs
// Let's define a simple model to track Rustlings exercise progress. Progress // Let's define a simple model to track Rustlings exercise progress. Progress
// will be modelled using a hash map. The name of the exercise is the key and // will be modelled using a hash map. The name of the exercise is the key and
// the progress is the value. Two counting functions were created to count the // the progress is the value. Two counting functions were created to count the
@ -7,8 +6,7 @@
// imperative style for loops. Recreate this counting functionality using // imperative style for loops. Recreate this counting functionality using
// iterators. Only the two iterator methods (count_iterator and // iterators. Only the two iterator methods (count_iterator and
// count_collection_iterator) need to be modified. // count_collection_iterator) need to be modified.
// Execute `rustlings hint // Execute `rustlings hint iterators5` for hints.
// iterators5` for hints.
// //
// Make the code compile and the tests pass. // Make the code compile and the tests pass.
@ -16,7 +14,7 @@
use std::collections::HashMap; use std::collections::HashMap;
#[derive(PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
enum Progress { enum Progress {
None, None,
Some, Some,

View file

@ -16,13 +16,13 @@ struct Package {
impl Package { impl Package {
fn new(sender_country: String, recipient_country: String, weight_in_grams: i32) -> Package { fn new(sender_country: String, recipient_country: String, weight_in_grams: i32) -> Package {
if weight_in_grams <= 0 { if weight_in_grams <= 0 {
// Something goes here... // panic statement 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);

View file

@ -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")

View file

@ -1,10 +1,6 @@
// variables1.rs // variables1.rs
// Make me compile! Execute the command `rustlings hint variables1` if you want a hint :) // Make me compile!
// Execute the command `rustlings hint variables1` if you want a hint :)
// About this `I AM NOT DONE` thing:
// We sometimes encourage you to keep trying things on a given exercise,
// even after you already figured it out. If you got everything working and
// feel ready for the next exercise, remove the `I AM NOT DONE` comment below.
// I AM NOT DONE // I AM NOT DONE

View file

@ -4,7 +4,7 @@
// I AM NOT DONE // I AM NOT DONE
fn main() { fn main() {
let number = "T-H-R-E-E"; let number = "T-H-R-E-E"; // don't change this line
println!("Spell a Number : {}", number); println!("Spell a Number : {}", number);
number = 3; number = 3;
println!("Number plus two is : {}", number + 2); println!("Number plus two is : {}", number + 2);

243
info.toml
View file

@ -1,3 +1,19 @@
# INTRO
[[exercises]]
name = "intro1"
path = "exercises/intro/intro1.rs"
mode = "compile"
hint = """
Remove the I AM NOT DONE comment to move on to the next exercise."""
[[exercises]]
name = "intro2"
path = "exercises/intro/intro2.rs"
mode = "compile"
hint = """
Add an argument after the format string."""
# VARIABLES # VARIABLES
[[exercises]] [[exercises]]
@ -114,8 +130,7 @@ path = "exercises/functions/functions5.rs"
mode = "compile" mode = "compile"
hint = """ hint = """
This is a really common error that can be fixed by removing one character. This is a really common error that can be fixed by removing one character.
It happens because Rust distinguishes between expressions and statements: expressions return It happens because Rust distinguishes between expressions and statements: expressions return a value based on their operand(s), and statements simply return a () type which behaves just like `void` in C/C++ language.
a value based on its operand, and statements simply return a () type which behaves just like `void` in C/C++ language.
We want to return a value of `i32` type from the `square` function, but it is returning a `()` type... We want to return a value of `i32` type from the `square` function, but it is returning a `()` type...
They are not the same. There are two solutions: They are not the same. There are two solutions:
1. Add a `return` ahead of `num * num;` 1. Add a `return` ahead of `num * num;`
@ -210,6 +225,18 @@ So the end goal is to:
- since we're not creating a new vec in `main` anymore, we need to create - since we're not creating a new vec in `main` anymore, we need to create
a new vec in `fill_vec`, similarly to the way we did in `main`""" a new vec in `fill_vec`, similarly to the way we did in `main`"""
[[exercises]]
name = "move_semantics5"
path = "exercises/move_semantics/move_semantics5.rs"
mode = "compile"
hint = """
Carefully reason about the range in which each mutable reference is in
vogue. Does it help to update the value of referent (x) immediately after
the mutable reference is taken? Read more about 'Mutable References'
in the book's section References and Borrowing':
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
"""
# PRIMITIVE TYPES # PRIMITIVE TYPES
[[exercises]] [[exercises]]
@ -350,11 +377,19 @@ name = "modules2"
path = "exercises/modules/modules2.rs" path = "exercises/modules/modules2.rs"
mode = "compile" mode = "compile"
hint = """ hint = """
The delicious_snacks module is trying to present an external The delicious_snacks module is trying to present an external interface that is
interface (the `fruit` and `veggie` constants) that is different than different than its internal structure (the `fruits` and `veggies` modules and
its internal structure (the `fruits` and `veggies` modules and associated constants). Complete the `use` statements to fit the uses in main and
associated constants). It's almost there except for one keyword missing for find the one keyword missing for both constants."""
each constant."""
[[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 # COLLECTIONS
@ -475,42 +510,61 @@ hint = """
If other functions can return a `Result`, why shouldn't `main`?""" If other functions can return a `Result`, why shouldn't `main`?"""
[[exercises]] [[exercises]]
name = "errorsn" name = "errors4"
path = "exercises/error_handling/errorsn.rs" path = "exercises/error_handling/errors4.rs"
mode = "test" mode = "test"
hint = """ hint = """
First hint: To figure out what type should go where the ??? is, take a look `PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result.
at the test helper function `test_with_str`, since it returns whatever It should be doing some checking, returning an `Err` result if those checks fail, and only
`read_and_validate` returns and `test_with_str` has its signature fully returning an `Ok` result if those checks determine that everything is... okay :)"""
specified.
Next hint: There are three places in `read_and_validate` that we call a
function that returns a `Result` (that is, the functions might fail).
Apply the `?` operator on those calls so that we return immediately from
`read_and_validate` if those function calls fail.
[[exercises]]
name = "errors5"
path = "exercises/error_handling/errors5.rs"
mode = "compile"
hint = """
Hint: There are two different possible `Result` types produced within
`main()`, which are propagated using `?` operators. How do we declare a
return type from `main()` that allows both?
Another hint: under the hood, the `?` operator calls `From::from` Another hint: under the hood, the `?` operator calls `From::from`
on the error value to convert it to a boxed trait object, a Box<dyn error::Error>, on the error value to convert it to a boxed trait object, a
which is polymorphic-- that means that lots of different kinds of errors `Box<dyn error::Error>`, which is polymorphic-- that means that lots of
can be returned from the same function because all errors act the same different kinds of errors can be returned from the same function because
since they all implement the `error::Error` trait. all errors act the same since they all implement the `error::Error` trait.
Check out this section of the book: Check out this section of the book:
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
This exercise uses some concepts that we won't get to until later in the
course, like `Box` and the `From` trait. It's not important to understand
them in detail right now, but you can read ahead if you like.
Another another hint: Note that because the `?` operator returns Read more about boxing errors:
the *unwrapped* value in the `Ok` case, if we want to return a `Result` from https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
`read_and_validate` for *its* success case, we'll have to rewrap a value
that we got from the return value of a `?`ed call in an `Ok`-- this will
look like `Ok(something)`.
Read more about using the `?` operator with boxed errors:
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
"""
Another another another hint: `Result`s must be "used", that is, you'll [[exercises]]
get a warning if you don't handle a `Result` that you get in your name = "errors6"
function. Read more about that in the `std::result` module docs: path = "exercises/error_handling/errors6.rs"
https://doc.rust-lang.org/std/result/#results-must-be-used""" mode = "test"
hint = """
This exercise uses a completed version of `PositiveNonzeroInteger` from
errors4.
Below the line that TODO asks you to change, there is an example of using
the `map_err()` method on a `Result` to transform one type of error into
another. Try using something similar on the `Result` from `parse()`. You
might use the `?` operator to return early from the function, or you might
use a `match` expression, or maybe there's another way!
You can create another function inside `impl ParsePosNonzeroError` to use
with `map_err()`.
Read more about `map_err()` in the `std::result` documentation:
https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err"""
# Generics # Generics
@ -546,7 +600,7 @@ ReportCard struct generic, but also the correct property - you will need to chan
of the struct slightly too...you can do it! of the struct slightly too...you can do it!
""" """
# OPTIONS / RESULTS # OPTIONS
[[exercises]] [[exercises]]
name = "option1" name = "option1"
@ -579,13 +633,14 @@ Also see Option::flatten
""" """
[[exercises]] [[exercises]]
name = "result1" name = "option3"
path = "exercises/error_handling/result1.rs" path = "exercises/option/option3.rs"
mode = "test" mode = "compile"
hint = """ hint = """
`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. The compiler says a partial move happened in the `match`
It should be doing some checking, returning an `Err` result if those checks fail, and only statement. How can this be avoided? The compiler shows the correction
returning an `Ok` result if those checks determine that everything is... okay :)""" needed. After making the correction as suggested by the compiler, do
read: https://doc.rust-lang.org/std/keyword.ref.html"""
# TRAITS # TRAITS
@ -737,7 +792,10 @@ The division_results variable needs to be collected into a collection type.
The result_with_list function needs to return a single Result where the success The result_with_list function needs to return a single Result where the success
case is a vector of integers and the failure case is a DivisionError. case is a vector of integers and the failure case is a DivisionError.
The list_of_results function needs to return a vector of results.""" The list_of_results function needs to return a vector of results.
See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how
the `FromIterator` trait is used in `collect()`."""
[[exercises]] [[exercises]]
name = "iterators4" name = "iterators4"
@ -763,7 +821,10 @@ test count_iterator.
The collection variable in count_collection_iterator is a slice of HashMaps. It The collection variable in count_collection_iterator is a slice of HashMaps. It
needs to be converted into an iterator in order to use the iterator methods. needs to be converted into an iterator in order to use the iterator methods.
The fold method can be useful in the count_collection_iterator function.""" The fold method can be useful in the count_collection_iterator function.
For a further challenge, consult the documentation for Iterator to find
a different method that could make your code more compact than using fold."""
# THREADS # THREADS
@ -861,7 +922,15 @@ name = "clippy1"
path = "exercises/clippy/clippy1.rs" path = "exercises/clippy/clippy1.rs"
mode = "clippy" mode = "clippy"
hint = """ hint = """
Floating point calculations are usually imprecise, so asking if two values are exactly equal is asking for trouble""" Rust stores the highest precision version of any long or inifinite precision
mathematical constants in the rust standard library.
https://doc.rust-lang.org/stable/std/f32/consts/index.html
We may be tempted to use our own approximations for certain mathematical constants,
but clippy recognizes those imprecise mathematical constants as a source of
potential error.
See the suggestions of the clippy warning in compile output and use the
appropriate replacement constant from std::f32::consts..."""
[[exercises]] [[exercises]]
name = "clippy2" name = "clippy2"
@ -887,13 +956,48 @@ 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"
mode = "test" mode = "test"
hint = """ 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
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?
Another hint: Look at the test cases to see which error variants to return.
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]] [[exercises]]
name = "as_ref_mut" name = "as_ref_mut"
@ -902,11 +1006,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.
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.
"""

View file

@ -12,6 +12,18 @@ else
exit 1 exit 1
fi fi
if [ -x "$(command -v cc)" ]
then
echo "SUCCESS: cc is installed"
else
echo "ERROR: cc does not seem to be installed."
echo "Please download (g)cc using your package manager."
echo "OSX: xcode-select --install"
echo "Deb: sudo apt install gcc"
echo "Yum: sudo yum -y install gcc"
exit 1
fi
if [ -x "$(command -v rustc)" ] if [ -x "$(command -v rustc)" ]
then then
echo "SUCCESS: Rust is installed" echo "SUCCESS: Rust is installed"

View file

@ -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.
@ -154,7 +154,7 @@ path = "{}.rs""#,
Command::new("cargo") Command::new("cargo")
.args(&["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) .args(&["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
.args(RUSTC_COLOR_ARGS) .args(RUSTC_COLOR_ARGS)
.args(&["--", "-D", "warnings"]) .args(&["--", "-D", "warnings","-D","clippy::float_cmp"])
.output() .output()
} }
} }
@ -162,7 +162,7 @@ path = "{}.rs""#,
if cmd.status.success() { if cmd.status.success() {
Ok(CompiledExercise { Ok(CompiledExercise {
exercise: &self, exercise: self,
_handle: FileHandle, _handle: FileHandle,
}) })
} else { } else {
@ -217,8 +217,7 @@ path = "{}.rs""#,
let matched_line_index = source let matched_line_index = source
.lines() .lines()
.enumerate() .enumerate()
.filter_map(|(i, line)| if re.is_match(line) { Some(i) } else { None }) .find_map(|(i, line)| if re.is_match(line) { Some(i) } else { None })
.next()
.expect("This should not happen at all"); .expect("This should not happen at all");
let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize; let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize;

View file

@ -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.4.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
@ -154,9 +155,7 @@ fn main() {
"Pending" "Pending"
}; };
let solve_cond = { let solve_cond = {
(e.looks_done() && subargs.solved) (e.looks_done() && subargs.solved) || (!e.looks_done() && subargs.unsolved) || (!subargs.solved && !subargs.unsolved)
|| (!e.looks_done() && subargs.unsolved)
|| (!subargs.solved && !subargs.unsolved)
}; };
if solve_cond && (filter_cond || subargs.filter.is_none()) { if solve_cond && (filter_cond || subargs.filter.is_none()) {
let line = if subargs.paths { let line = if subargs.paths {
@ -194,7 +193,7 @@ fn main() {
Subcommands::Run(subargs) => { Subcommands::Run(subargs) => {
let exercise = find_exercise(&subargs.name, &exercises); 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) => { Subcommands::Hint(subargs) => {
@ -207,38 +206,50 @@ 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 {:?}.", e);
"Error: Could not watch your progress. Error message was {:?}.",
e
);
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} All exercises completed! {emoji}", emoji = Emoji("🎉", ""));
emoji = Emoji("🎉", "")
);
println!("\n{}\n", FENISH_LINE); println!("\n{}\n", FENISH_LINE);
} }
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);
} }
@ -249,16 +260,26 @@ fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>) {
} }
fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
exercises if name.eq("next") {
.iter() exercises.iter().find(|e| !e.looks_done()).unwrap_or_else(|| {
.find(|e| e.name == name) println!("🎉 Congratulations! You have done all the exercises!");
.unwrap_or_else(|| { println!("🔚 There are no more exercises to do next!");
std::process::exit(1)
})
} else {
exercises.iter().find(|e| e.name == name).unwrap_or_else(|| {
println!("No exercise found for '{}'!", name); println!("No exercise found for '{}'!", name);
std::process::exit(1) std::process::exit(1)
}) })
} }
}
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() {
@ -266,6 +287,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)?;
@ -274,12 +296,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() {
@ -288,14 +310,10 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
.iter() .iter()
.skip_while(|e| !filepath.ends_with(&e.path)) .skip_while(|e| !filepath.ends_with(&e.path))
// .filter(|e| filepath.ends_with(&e.path)) // .filter(|e| filepath.ends_with(&e.path))
.chain( .chain(exercises.iter().filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)));
exercises
.iter()
.filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)),
);
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));
@ -305,8 +323,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);
}
} }
} }

View file

@ -14,9 +14,9 @@ pub fn verify<'a>(
) -> Result<(), &'a Exercise> { ) -> Result<(), &'a Exercise> {
for exercise in start_at { for exercise in start_at {
let compile_result = match exercise.mode { let compile_result = match exercise.mode {
Mode::Test => compile_and_test(&exercise, RunMode::Interactive, verbose), Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose),
Mode::Compile => compile_and_run_interactively(&exercise), Mode::Compile => compile_and_run_interactively(exercise),
Mode::Clippy => compile_only(&exercise), Mode::Clippy => compile_only(exercise),
}; };
if !compile_result.unwrap_or(false) { if !compile_result.unwrap_or(false) {
return Err(exercise); return Err(exercise);
@ -42,11 +42,11 @@ fn compile_only(exercise: &Exercise) -> Result<bool, ()> {
progress_bar.set_message(format!("Compiling {}...", exercise).as_str()); progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
progress_bar.enable_steady_tick(100); progress_bar.enable_steady_tick(100);
let _ = compile(&exercise, &progress_bar)?; let _ = compile(exercise, &progress_bar)?;
progress_bar.finish_and_clear(); progress_bar.finish_and_clear();
success!("Successfully compiled {}!", exercise); 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 // 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<bool, ()> {
progress_bar.set_message(format!("Compiling {}...", exercise).as_str()); progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
progress_bar.enable_steady_tick(100); 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()); progress_bar.set_message(format!("Running {}...", exercise).as_str());
let result = compilation.run(); let result = compilation.run();
@ -73,7 +73,7 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result<bool, ()> {
success!("Successfully ran {}!", exercise); 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 // 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); success!("Successfully tested {}", &exercise);
if let RunMode::Interactive = run_mode { if let RunMode::Interactive = run_mode {
Ok(prompt_for_completion(&exercise, None)) Ok(prompt_for_completion(exercise, None))
} else { } else {
Ok(true) Ok(true)
} }