mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-12-26 00:11:49 +02:00
Compare commits
82 Commits
rustlings-
...
v6.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0847b3a4bf | ||
|
|
6d2ea8dae3 | ||
|
|
47ba4502e0 | ||
|
|
6263cb6456 | ||
|
|
d7024d80ce | ||
|
|
59d6b852af | ||
|
|
e512928df5 | ||
|
|
a38029e3e7 | ||
|
|
b12b652a57 | ||
|
|
c793416495 | ||
|
|
01343f187b | ||
|
|
69021e1497 | ||
|
|
08c408aae0 | ||
|
|
bf698659b0 | ||
|
|
2d5d70693a | ||
|
|
a4091ade5c | ||
|
|
a7a881809f | ||
|
|
0f4cb94cfe | ||
|
|
6469e9734b | ||
|
|
5372caefb3 | ||
|
|
9d7b973a62 | ||
|
|
a5f221aa39 | ||
|
|
e764b75aef | ||
|
|
708cfef3f7 | ||
|
|
01b8432d58 | ||
|
|
9b5b652c71 | ||
|
|
981a4778a9 | ||
|
|
5d4363d58d | ||
|
|
48697b8225 | ||
|
|
1652bb67d9 | ||
|
|
1499f681a3 | ||
|
|
a21fa6ff40 | ||
|
|
186dc3c1ab | ||
|
|
6b7a27d080 | ||
|
|
c9017f9f7a | ||
|
|
fdada8b3d4 | ||
|
|
9e2ff7d037 | ||
|
|
65834fc420 | ||
|
|
f5a4965de7 | ||
|
|
db5911eb73 | ||
|
|
2f4e63b443 | ||
|
|
584164a6ff | ||
|
|
e6f6d26d13 | ||
|
|
67d8d5848c | ||
|
|
43d15f09f0 | ||
|
|
7123c7ae3a | ||
|
|
4d9c346a17 | ||
|
|
deed9d3943 | ||
|
|
652f0c7676 | ||
|
|
e479ec8fb6 | ||
|
|
a33501e6a7 | ||
|
|
47f8199a99 | ||
|
|
4bf0ddc0e1 | ||
|
|
4cd0ccce83 | ||
|
|
a3657188b6 | ||
|
|
b8fcd11286 | ||
|
|
4810555038 | ||
|
|
84b291852c | ||
|
|
74831dd88f | ||
|
|
0b220f9fff | ||
|
|
d3cdeed871 | ||
|
|
0524632199 | ||
|
|
30d5b7db92 | ||
|
|
2f60f4d9ea | ||
|
|
b017b87866 | ||
|
|
b87aa98634 | ||
|
|
a4c07ca948 | ||
|
|
b8826dd3b3 | ||
|
|
d54c050985 | ||
|
|
248dd4415e | ||
|
|
dec6772b05 | ||
|
|
b4f4c79ac4 | ||
|
|
c1d5252b87 | ||
|
|
0f71e12235 | ||
|
|
fa6b7d77b2 | ||
|
|
a72c26bdc3 | ||
|
|
fe3292c170 | ||
|
|
ad66fe0074 | ||
|
|
df64893f2b | ||
|
|
e5bc8588e0 | ||
|
|
23bc5d23fe | ||
|
|
77b687d501 |
23
.github/workflows/rust.yml
vendored
23
.github/workflows/rust.yml
vendored
@@ -18,25 +18,26 @@ jobs:
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- uses: DavidAnson/markdownlint-cli2-action@v9
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DavidAnson/markdownlint-cli2-action@v16
|
||||
with:
|
||||
globs: "exercises/**/*.md"
|
||||
- name: Run cargo fmt
|
||||
run: |
|
||||
cargo fmt --all -- --check
|
||||
run: cargo fmt --all -- --check
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: actions/checkout@v4
|
||||
- uses: swatinem/rust-cache@v2
|
||||
- name: Run cargo test
|
||||
run: |
|
||||
cargo test
|
||||
run: cargo test
|
||||
dev-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: swatinem/rust-cache@v2
|
||||
- name: Run rustlings dev check
|
||||
run: cargo run -- dev check --require-solutions
|
||||
|
||||
3
.github/workflows/web.yml
vendored
3
.github/workflows/web.yml
vendored
@@ -54,10 +54,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Setup
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: swatinem/rust-cache@v2
|
||||
|
||||
# If you use any mdbook plugins, here's the place to install them!
|
||||
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,3 +1,29 @@
|
||||
<a name="6.1.0"></a>
|
||||
|
||||
## 6.1.0 (2024-07-10)
|
||||
|
||||
#### Added
|
||||
|
||||
- `dev check`: Check that all exercises (including third-party ones) include at least one `TODO` comment.
|
||||
- `dev check`: Check that all exercises actually fail to run (not already solved).
|
||||
|
||||
#### Changed
|
||||
|
||||
- Make enum variants more consistent between enum exercises.
|
||||
- `iterators3`: Teach about the possible case of integer overflow during division.
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Exit with a helpful error message on missing/unsupported terminal/TTY.
|
||||
- Mark the last exercise as done.
|
||||
|
||||
<a name="6.0.1"></a>
|
||||
|
||||
## 6.0.1 (2024-07-04)
|
||||
|
||||
Small exercise improvements and fixes.
|
||||
Most importantly, fixed that the exercise `clippy1` was already solved 😅
|
||||
|
||||
<a name="6.0.0"></a>
|
||||
|
||||
## 6.0.0 (2024-07-03)
|
||||
@@ -57,7 +83,7 @@ This should avoid issues related to the language server or to running exercises,
|
||||
Clippy lints are now shown on all exercises, not only the Clippy exercises 📎
|
||||
Make Clippy your friend from early on 🥰
|
||||
|
||||
### Third party exercises
|
||||
### Third-party exercises
|
||||
|
||||
Rustlings now supports third-party exercises!
|
||||
|
||||
|
||||
108
Cargo.lock
generated
108
Cargo.lock
generated
@@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
@@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.8"
|
||||
version = "4.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
|
||||
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -161,9 +161,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.8"
|
||||
version = "4.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
|
||||
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -354,15 +354,6 @@ version = "1.70.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
@@ -527,7 +518,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall 0.5.2",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -594,7 +585,7 @@ dependencies = [
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm",
|
||||
"itertools 0.13.0",
|
||||
"itertools",
|
||||
"lru",
|
||||
"paste",
|
||||
"stability",
|
||||
@@ -654,7 +645,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "rustlings"
|
||||
version = "6.0.0"
|
||||
version = "6.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
@@ -673,7 +664,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustlings-macros"
|
||||
version = "6.0.0"
|
||||
version = "6.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
@@ -709,18 +700,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -785,9 +776,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "stability"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a"
|
||||
checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -829,9 +820,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.68"
|
||||
version = "2.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -855,9 +846,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.14"
|
||||
version = "0.22.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||
checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@@ -880,11 +871,12 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-truncate"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
|
||||
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
||||
dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"itertools",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
@@ -977,7 +969,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -997,18 +989,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.5",
|
||||
"windows_aarch64_msvc 0.52.5",
|
||||
"windows_i686_gnu 0.52.5",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.5",
|
||||
"windows_x86_64_gnu 0.52.5",
|
||||
"windows_x86_64_gnullvm 0.52.5",
|
||||
"windows_x86_64_msvc 0.52.5",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1019,9 +1011,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@@ -1031,9 +1023,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -1043,15 +1035,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -1061,9 +1053,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -1073,9 +1065,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@@ -1085,9 +1077,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -1097,9 +1089,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -8,7 +8,7 @@ exclude = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "6.0.0"
|
||||
version = "6.1.0"
|
||||
authors = [
|
||||
"Liv <mokou@fastmail.com>",
|
||||
"Mo Bitar <mo8it@proton.me>",
|
||||
@@ -20,8 +20,8 @@ license = "MIT"
|
||||
edition = "2021"
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] }
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
toml_edit = { version = "0.22.15", default-features = false, features = ["parse", "serde"] }
|
||||
|
||||
[package]
|
||||
name = "rustlings"
|
||||
@@ -47,13 +47,13 @@ include = [
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
clap = { version = "4.5.8", features = ["derive"] }
|
||||
clap = { version = "4.5.9", features = ["derive"] }
|
||||
crossterm = "0.27.0"
|
||||
hashbrown = "0.14.5"
|
||||
notify-debouncer-mini = { version = "0.4.1", default-features = false }
|
||||
os_pipe = "1.2.0"
|
||||
ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] }
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.0.0" }
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.1.0" }
|
||||
serde_json = "1.0.120"
|
||||
serde.workspace = true
|
||||
toml_edit.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ It contains code examples and exercises similar to Rustlings, but online.
|
||||
|
||||
Before installing Rustlings, you need to have _Rust installed_.
|
||||
Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust.
|
||||
This'll also install _Cargo_, Rust's package/project manager.
|
||||
This will also install _Cargo_, Rust's package/project manager.
|
||||
|
||||
> 🐧 If you're on Linux, make sure you've installed `gcc` (for a linker).
|
||||
>
|
||||
@@ -124,7 +124,7 @@ Continue practicing your Rust skills by building your own projects, contributing
|
||||
|
||||
Do you want to create your own set of Rustlings exercises to focus on some specific topic?
|
||||
Or do you want to translate the original Rustlings exercises?
|
||||
Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)!
|
||||
Then follow the link to the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)!
|
||||
|
||||
## Uninstalling Rustlings
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Third-Party Exercises
|
||||
|
||||
The support of Rustlings for third-party exercises allows you to create your own set of Rustlings exercises to focus on some specific topic.
|
||||
You could also offer a translatation of the original Rustlings exercises as third-party exercises.
|
||||
You could also offer a translation of the original Rustlings exercises as third-party exercises.
|
||||
|
||||
## Getting started
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
This file is used to check if the user tries to run Rustlings in the repository (the method before v6)
|
||||
This file is used to check if the user tries to run Rustlings in the repository (the method before version 6)
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
// ready for the next exercise, enter `n` in the terminal.
|
||||
//
|
||||
// The exercise file will be reloaded when you change one of the lines below!
|
||||
// Try adding a new `println!`.
|
||||
// Try removing a semicolon and see what happens in the terminal!
|
||||
// Try adding a new `println!` and check the updated output in the terminal.
|
||||
|
||||
fn main() {
|
||||
println!("Hello and");
|
||||
|
||||
@@ -5,5 +5,5 @@ compiler. In this section, we'll go through the most important ones.
|
||||
|
||||
## Further information
|
||||
|
||||
- [Data Types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html)
|
||||
- [The Slice Type](https://doc.rust-lang.org/stable/book/ch04-03-slices.html)
|
||||
- [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html)
|
||||
- [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html)
|
||||
|
||||
@@ -12,6 +12,6 @@ the other useful data structure, hash maps, later.
|
||||
|
||||
## 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/book/ch08-01-vectors.html)
|
||||
- [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut)
|
||||
- [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map)
|
||||
|
||||
@@ -7,12 +7,12 @@ mod tests {
|
||||
// TODO: Fix the compiler errors only by reordering the lines in the test.
|
||||
// Don't add, change or remove any line.
|
||||
#[test]
|
||||
fn move_semantics5() {
|
||||
let mut x = 100;
|
||||
fn move_semantics4() {
|
||||
let mut x = Vec::new();
|
||||
let y = &mut x;
|
||||
let z = &mut x;
|
||||
*y += 100;
|
||||
*z += 1000;
|
||||
assert_eq!(x, 1200);
|
||||
y.push(42);
|
||||
z.push(13);
|
||||
assert_eq!(x, [42, 13]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
#![allow(clippy::ptr_arg)]
|
||||
|
||||
// TODO: Fix the compiler errors without changing anything except adding or
|
||||
// removing references (the character `&`).
|
||||
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
get_char(data);
|
||||
|
||||
string_uppercase(&data);
|
||||
}
|
||||
|
||||
// Shouldn't take ownership
|
||||
fn get_char(data: String) -> char {
|
||||
data.chars().last().unwrap()
|
||||
@@ -16,7 +10,15 @@ fn get_char(data: String) -> char {
|
||||
|
||||
// Should take ownership
|
||||
fn string_uppercase(mut data: &String) {
|
||||
data = &data.to_uppercase();
|
||||
data = data.to_uppercase();
|
||||
|
||||
println!("{data}");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
get_char(data);
|
||||
|
||||
string_uppercase(&data);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Structs contain data, but can also have logic. In this exercise we have
|
||||
// defined the `Package` struct and we want to test some logic attached to it.
|
||||
// Structs contain data, but can also have logic. In this exercise, we have
|
||||
// defined the `Package` struct, and we want to test some logic attached to it.
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Package {
|
||||
|
||||
@@ -4,8 +4,9 @@ enum Message {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", Message::Quit);
|
||||
println!("{:?}", Message::Echo);
|
||||
println!("{:?}", Message::Resize);
|
||||
println!("{:?}", Message::Move);
|
||||
println!("{:?}", Message::Echo);
|
||||
println!("{:?}", Message::ChangeColor);
|
||||
println!("{:?}", Message::Quit);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
// TODO: Define the different variants used below.
|
||||
@@ -5,13 +13,17 @@ enum Message {
|
||||
|
||||
impl Message {
|
||||
fn call(&self) {
|
||||
println!("{:?}", self);
|
||||
println!("{self:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let messages = [
|
||||
Message::Move { x: 10, y: 30 },
|
||||
Message::Resize {
|
||||
width: 10,
|
||||
height: 30,
|
||||
},
|
||||
Message::Move(Point { x: 10, y: 15 }),
|
||||
Message::Echo(String::from("hello world")),
|
||||
Message::ChangeColor(200, 255, 255),
|
||||
Message::Quit,
|
||||
|
||||
@@ -1,40 +1,47 @@
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
enum Message {
|
||||
// TODO: Implement the message variant types based on their usage below.
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x: u8,
|
||||
y: u8,
|
||||
}
|
||||
|
||||
struct State {
|
||||
color: (u8, u8, u8),
|
||||
width: u64,
|
||||
height: u64,
|
||||
position: Point,
|
||||
quit: bool,
|
||||
message: String,
|
||||
// RGB color composed of red, green and blue.
|
||||
color: (u8, u8, u8),
|
||||
quit: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn change_color(&mut self, color: (u8, u8, u8)) {
|
||||
self.color = color;
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
self.quit = true;
|
||||
}
|
||||
|
||||
fn echo(&mut self, s: String) {
|
||||
self.message = s;
|
||||
fn resize(&mut self, width: u64, height: u64) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
}
|
||||
|
||||
fn move_position(&mut self, point: Point) {
|
||||
self.position = point;
|
||||
}
|
||||
|
||||
fn echo(&mut self, s: String) {
|
||||
self.message = s;
|
||||
}
|
||||
|
||||
fn change_color(&mut self, red: u8, green: u8, blue: u8) {
|
||||
self.color = (red, green, blue);
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
self.quit = true;
|
||||
}
|
||||
|
||||
fn process(&mut self, message: Message) {
|
||||
// TODO: Create a match expression to process the different message variants.
|
||||
// Remember: When passing a tuple as a function argument, you'll need extra parentheses:
|
||||
// e.g. `foo((t, u, p, l, e))`
|
||||
// TODO: Create a match expression to process the different message
|
||||
// variants using the methods defined above.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,21 +56,29 @@ mod tests {
|
||||
#[test]
|
||||
fn test_match_message_call() {
|
||||
let mut state = State {
|
||||
quit: false,
|
||||
width: 0,
|
||||
height: 0,
|
||||
position: Point { x: 0, y: 0 },
|
||||
color: (0, 0, 0),
|
||||
message: String::from("hello world"),
|
||||
color: (0, 0, 0),
|
||||
quit: false,
|
||||
};
|
||||
|
||||
state.process(Message::ChangeColor(255, 0, 255));
|
||||
state.process(Message::Echo(String::from("Hello world!")));
|
||||
state.process(Message::Resize {
|
||||
width: 10,
|
||||
height: 30,
|
||||
});
|
||||
state.process(Message::Move(Point { x: 10, y: 15 }));
|
||||
state.process(Message::Echo(String::from("Hello world!")));
|
||||
state.process(Message::ChangeColor(255, 0, 255));
|
||||
state.process(Message::Quit);
|
||||
|
||||
assert_eq!(state.color, (255, 0, 255));
|
||||
assert_eq!(state.width, 10);
|
||||
assert_eq!(state.height, 30);
|
||||
assert_eq!(state.position.x, 10);
|
||||
assert_eq!(state.position.y, 15);
|
||||
assert!(state.quit);
|
||||
assert_eq!(state.message, "Hello world!");
|
||||
assert_eq!(state.color, (255, 0, 255));
|
||||
assert!(state.quit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ fn placeholder() {}
|
||||
fn string_slice(arg: &str) {
|
||||
println!("{arg}");
|
||||
}
|
||||
|
||||
fn string(arg: String) {
|
||||
println!("{arg}");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// You can bring module paths into scopes and provide new names for them with
|
||||
// the `use` and `as` keywords.
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod delicious_snacks {
|
||||
// TODO: Add the following two `use` statements after fixing them.
|
||||
// use self::fruits::PEAR as ???;
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You
|
||||
// must add fruit to the basket so that there is at least one of each kind and
|
||||
// more than 11 in total - we have a lot of mouths to feed. You are not allowed
|
||||
// to insert any more of these fruits!
|
||||
// to insert any more of the fruits that are already in the basket (Apple,
|
||||
// Mango, and Lyche).
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Option types are very common in Rust code, as they have a number of uses:
|
||||
|
||||
## Further Information
|
||||
|
||||
- [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/book/ch10-01-syntax.html#in-enum-definitions)
|
||||
- [Option Module Documentation](https://doc.rust-lang.org/std/option/)
|
||||
- [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)
|
||||
|
||||
@@ -19,7 +19,8 @@ mod tests {
|
||||
// TODO: Fix this test. How do you get the value contained in the
|
||||
// Option?
|
||||
let icecreams = maybe_icecream(12);
|
||||
assert_eq!(icecreams, 5);
|
||||
|
||||
assert_eq!(icecreams, 5); // Don't change this line.
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
// the player typed in the quantity, we get it as a string. They might have
|
||||
// typed anything, not just numbers!
|
||||
//
|
||||
// Right now, this function isn't handling the error case at all (and isn't
|
||||
// handling the success case properly either). What we want to do is: If we call
|
||||
// the `total_cost` function on a string that is not a number, that function
|
||||
// will return a `ParseIntError`. In that case, we want to immediately return
|
||||
// that error from our function and not try to multiply and add.
|
||||
// Right now, this function isn't handling the error case at all. What we want
|
||||
// to do is: If we call the `total_cost` function on a string that is not a
|
||||
// number, that function will return a `ParseIntError`. In that case, we want to
|
||||
// immediately return that error from our function and not try to multiply and
|
||||
// add.
|
||||
//
|
||||
// There are at least two ways to implement this that are both correct. But one
|
||||
// is a lot shorter!
|
||||
|
||||
@@ -19,6 +19,7 @@ fn main() {
|
||||
let mut tokens = 100;
|
||||
let pretend_user_input = "8";
|
||||
|
||||
// Don't change this line.
|
||||
let cost = total_cost(pretend_user_input)?;
|
||||
|
||||
if cost > tokens {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::comparison_chain)]
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
|
||||
@@ -39,8 +39,8 @@ struct PositiveNonzeroInteger(u64);
|
||||
impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
||||
match value {
|
||||
0 => Err(CreationError::Zero),
|
||||
x if x < 0 => Err(CreationError::Negative),
|
||||
0 => Err(CreationError::Zero),
|
||||
x => Ok(PositiveNonzeroInteger(x as u64)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<Self, CreationError> {
|
||||
match value {
|
||||
x if x < 0 => Err(CreationError::Negative),
|
||||
x if x == 0 => Err(CreationError::Zero),
|
||||
0 => Err(CreationError::Zero),
|
||||
x => Ok(Self(x as u64)),
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,6 @@ fn main() {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::num::IntErrorKind;
|
||||
|
||||
#[test]
|
||||
fn test_parse_error() {
|
||||
|
||||
@@ -7,5 +7,5 @@ The simplest and most common use of generics is for type parameters.
|
||||
|
||||
## Further information
|
||||
|
||||
- [Generic Data Types](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html)
|
||||
- [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html)
|
||||
- [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
trait Licensed {
|
||||
// TODO: Add a default implementation for `licensing_info` so that
|
||||
// implementors like the two structs below can share that default behavior
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum DivisionError {
|
||||
// Example: 42 / 0
|
||||
DivideByZero,
|
||||
// Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
|
||||
IntegerOverflow,
|
||||
// Example: 5 / 2 = 2.5
|
||||
NotDivisible,
|
||||
}
|
||||
|
||||
// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
|
||||
// Otherwise, return a suitable error.
|
||||
fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
|
||||
fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
@@ -42,6 +46,11 @@ mod tests {
|
||||
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integer_overflow() {
|
||||
assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_divisible() {
|
||||
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));
|
||||
@@ -54,7 +63,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_result_with_list() {
|
||||
assert_eq!(result_with_list().unwarp(), [1, 11, 1426, 3]);
|
||||
assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
fn factorial(num: u8) -> u64 {
|
||||
// TODO: Complete this function to return the factorial of `num`.
|
||||
fn factorial(num: u64) -> u64 {
|
||||
// TODO: Complete this function to return the factorial of `num` which is
|
||||
// defined as `1 * 2 * 3 * … * num`.
|
||||
// https://en.wikipedia.org/wiki/Factorial
|
||||
//
|
||||
// Do not use:
|
||||
// - early returns (using the `return` keyword explicitly)
|
||||
// Try not to use:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// In this exercise, we are given a `Vec` of u32 called `numbers` with values
|
||||
// In this exercise, we are given a `Vec` of `u32` called `numbers` with values
|
||||
// ranging from 0 to 99. We would like to use this set of numbers within 8
|
||||
// different threads simultaneously. Each thread is going to get the sum of
|
||||
// every eighth value with an offset.
|
||||
@@ -9,8 +9,11 @@
|
||||
// …
|
||||
// The eighth thread (offset 7), will sum 7, 15, 23, …
|
||||
//
|
||||
// Because we are using threads, our values need to be thread-safe. Therefore,
|
||||
// we are using `Arc`.
|
||||
// Each thread should own a reference-counting pointer to the vector of
|
||||
// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`.
|
||||
//
|
||||
// Don't get distracted by how threads are spawned and joined. We will practice
|
||||
// that later in the exercises about threads.
|
||||
|
||||
// Don't change the lines below.
|
||||
#![forbid(unused_imports)]
|
||||
|
||||
@@ -45,8 +45,9 @@ mod tests {
|
||||
#[test]
|
||||
fn owned_no_mutation() {
|
||||
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
|
||||
// case, no mutation occurs and thus also no clone. But the result is
|
||||
// still owned because it was never borrowed or mutated.
|
||||
// case, no mutation occurs (all numbers are already absolute) and thus
|
||||
// also no clone. But the result is still owned because it was never
|
||||
// borrowed or mutated.
|
||||
let vec = vec![0, 1, 2];
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
@@ -56,9 +57,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn owned_mutation() {
|
||||
// Of course this is also the case if a mutation does occur. In this
|
||||
// case, the call to `to_mut()` in the `abs_all` function returns a
|
||||
// reference to the same data as before.
|
||||
// Of course this is also the case if a mutation does occur (not all
|
||||
// numbers are absolute). In this case, the call to `to_mut()` in the
|
||||
// `abs_all` function returns a reference to the same data as before.
|
||||
let vec = vec![-1, 0, 1];
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::rc::Rc;
|
||||
#[derive(Debug)]
|
||||
struct Sun;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
enum Planet {
|
||||
Mercury(Rc<Sun>),
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
// For these exercises, the code will fail to compile when there are Clippy
|
||||
// warnings. Check Clippy's suggestions from the output to solve the exercise.
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main() {
|
||||
// Use the more accurate `PI` constant.
|
||||
let pi = PI;
|
||||
// TODO: Fix the Clippy lint in this line.
|
||||
let pi = 3.14;
|
||||
let radius: f32 = 5.0;
|
||||
|
||||
let area = pi * radius.powi(2);
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
//
|
||||
// Mary is buying apples. The price of an apple is calculated as follows:
|
||||
// - An apple costs 2 rustbucks.
|
||||
// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck!
|
||||
// Write a function that calculates the price of an order of apples given the
|
||||
// quantity bought.
|
||||
// - However, if Mary buys more than 40 apples, the price of each apple in the
|
||||
// entire order is reduced to only 1 rustbuck!
|
||||
|
||||
// Put your function here!
|
||||
// fn calculate_price_of_apples(???) -> ??? {
|
||||
// TODO: Write a function that calculates the price of an order of apples given
|
||||
// the quantity bought.
|
||||
// fn calculate_price_of_apples(???) -> ??? { ??? }
|
||||
|
||||
fn main() {
|
||||
// You can optionally experiment here.
|
||||
|
||||
11
oranda.json
11
oranda.json
@@ -9,16 +9,5 @@
|
||||
"domain": "rustlings.cool"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"artifacts": {
|
||||
"auto": true,
|
||||
"package_managers": {
|
||||
"preferred": {
|
||||
"macos/linux/unix": "curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash",
|
||||
"windows": "Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,14 @@ get started, here are some notes about how Rustlings operates:
|
||||
3. If you're stuck on an exercise, enter `h` to show a hint.
|
||||
4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub!
|
||||
(https://github.com/rust-lang/rustlings). We look at every issue, and sometimes,
|
||||
other learners do too so you can help each other out!
|
||||
"""
|
||||
other learners do too so you can help each other out!"""
|
||||
|
||||
final_message = """We hope you enjoyed learning about the various aspects of Rust!
|
||||
If you noticed any issues, don't hesitate to report them on Github.
|
||||
You can also contribute your own exercises to help the greater community!
|
||||
|
||||
Before reporting an issue or contributing, please read our guidelines:
|
||||
https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md
|
||||
"""
|
||||
https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"""
|
||||
|
||||
# INTRO
|
||||
|
||||
@@ -33,6 +31,7 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md
|
||||
name = "intro1"
|
||||
dir = "00_intro"
|
||||
test = false
|
||||
skip_check_unsolved = true
|
||||
hint = """
|
||||
Enter `n` to move on to the next exercise.
|
||||
You might need to press ENTER after typing `n`."""
|
||||
@@ -130,8 +129,7 @@ The type of Constants must always be annotated.
|
||||
|
||||
Read more about constants and the differences between variables and constants
|
||||
under 'Constants' in the book's section 'Variables and Mutability':
|
||||
https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants
|
||||
"""
|
||||
https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants"""
|
||||
|
||||
# FUNCTIONS
|
||||
|
||||
@@ -311,9 +309,8 @@ In Rust, there are two ways to define a Vector.
|
||||
inside the square brackets. This way is simpler when you exactly know
|
||||
the initial values.
|
||||
|
||||
Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html
|
||||
of the Rust book to learn more.
|
||||
"""
|
||||
Check this chapter: https://doc.rust-lang.org/book/ch08-01-vectors.html
|
||||
of the Rust book to learn more."""
|
||||
|
||||
[[exercises]]
|
||||
name = "vecs2"
|
||||
@@ -327,8 +324,7 @@ In the second function, we map the values of the input and collect them into a v
|
||||
After you've completed both functions, decide for yourself which approach you
|
||||
like better.
|
||||
|
||||
What do you think is the more commonly used pattern under Rust developers?
|
||||
"""
|
||||
What do you think is the more commonly used pattern under Rust developers?"""
|
||||
|
||||
# MOVE SEMANTICS
|
||||
|
||||
@@ -355,8 +351,7 @@ We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's
|
||||
being "moved" into `vec1`, meaning we can't access `vec0` anymore.
|
||||
|
||||
You could make another, separate version of the data that's in `vec0` and
|
||||
pass it to `fill_vec` instead.
|
||||
"""
|
||||
pass it to `fill_vec` instead. This is called cloning in Rust."""
|
||||
|
||||
[[exercises]]
|
||||
name = "move_semantics3"
|
||||
@@ -375,8 +370,7 @@ Carefully reason about the range in which each mutable reference is in
|
||||
scope. Does it help to update the value of `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.
|
||||
"""
|
||||
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references."""
|
||||
|
||||
[[exercises]]
|
||||
name = "move_semantics5"
|
||||
@@ -384,7 +378,7 @@ dir = "06_move_semantics"
|
||||
test = false
|
||||
hint = """
|
||||
To find the answer, you can consult the book section "References and Borrowing":
|
||||
https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html
|
||||
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html
|
||||
|
||||
The first problem is that `get_char` is taking ownership of the string. So
|
||||
`data` is moved and can't be used for `string_uppercase`. `data` is moved to
|
||||
@@ -422,7 +416,7 @@ to its fields.
|
||||
|
||||
There are however some shortcuts that can be taken when instantiating structs.
|
||||
Have a look in The Book to find out more:
|
||||
https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
|
||||
https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
|
||||
|
||||
[[exercises]]
|
||||
name = "structs3"
|
||||
@@ -451,7 +445,7 @@ dir = "08_enums"
|
||||
test = false
|
||||
hint = """
|
||||
You can create enumerations that have different variants with different types
|
||||
such as no data, anonymous structs, a single string, tuples, etc."""
|
||||
such as anonymous structs, structs, a single string, tuples, no data, etc."""
|
||||
|
||||
[[exercises]]
|
||||
name = "enums3"
|
||||
@@ -493,7 +487,7 @@ to add one character to the `if` statement, though, that will coerce the
|
||||
Side note: If you're interested in learning about how this kind of reference
|
||||
conversion works, you can jump ahead in the book and read this part in the
|
||||
smart pointers chapter:
|
||||
https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods"""
|
||||
https://doc.rust-lang.org/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods"""
|
||||
|
||||
[[exercises]]
|
||||
name = "strings3"
|
||||
@@ -517,8 +511,7 @@ Example:
|
||||
`placeholder("blue");`
|
||||
should become
|
||||
`string_slice("blue");`
|
||||
because "blue" is `&str`, not `String`.
|
||||
"""
|
||||
because "blue" is `&str`, not `String`."""
|
||||
|
||||
# MODULES
|
||||
|
||||
@@ -568,7 +561,7 @@ hint = """
|
||||
Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this.
|
||||
|
||||
Learn more in The Book:
|
||||
https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value"""
|
||||
https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value"""
|
||||
|
||||
[[exercises]]
|
||||
name = "hashmaps3"
|
||||
@@ -579,7 +572,7 @@ Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of
|
||||
exist in the table yet.
|
||||
|
||||
Learn more in The Book:
|
||||
https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
|
||||
https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
|
||||
|
||||
Hint 2: If there is already an entry for a given key, the value returned by
|
||||
`entry()` can be updated based on the existing value.
|
||||
@@ -592,7 +585,7 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-
|
||||
[[exercises]]
|
||||
name = "quiz2"
|
||||
dir = "quizzes"
|
||||
hint = "No hints this time ;)"
|
||||
hint = "The `+` operator can concatenate a `String` with a `&str`."
|
||||
|
||||
# OPTIONS
|
||||
|
||||
@@ -620,8 +613,7 @@ Remember that `Option`s can be nested in if-let and while-let statements.
|
||||
|
||||
For example: `if let Some(Some(x)) = y`
|
||||
|
||||
Also see `Option::flatten`
|
||||
"""
|
||||
Also see `Option::flatten`"""
|
||||
|
||||
[[exercises]]
|
||||
name = "options3"
|
||||
@@ -747,7 +739,7 @@ name = "generics2"
|
||||
dir = "14_generics"
|
||||
hint = """
|
||||
Related section in The Book:
|
||||
https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions"""
|
||||
https://doc.rust-lang.org/book/ch10-01-syntax.html#in-method-definitions"""
|
||||
|
||||
# TRAITS
|
||||
|
||||
@@ -756,7 +748,9 @@ name = "traits1"
|
||||
dir = "15_traits"
|
||||
hint = """
|
||||
More about traits in The Book:
|
||||
https://doc.rust-lang.org/book/ch10-02-traits.html"""
|
||||
https://doc.rust-lang.org/book/ch10-02-traits.html
|
||||
|
||||
The `+` operator can concatenate a `String` with a `&str`."""
|
||||
|
||||
[[exercises]]
|
||||
name = "traits2"
|
||||
@@ -813,8 +807,7 @@ Here is how to specify a trait bound for an implementation block:
|
||||
`impl<T: Trait1 + Trait2 + …> for Foo<T> { … }`
|
||||
|
||||
You may need this:
|
||||
`use std::fmt::Display;`
|
||||
"""
|
||||
`use std::fmt::Display;`"""
|
||||
|
||||
# LIFETIMES
|
||||
|
||||
@@ -878,7 +871,7 @@ We expect the method `Rectangle::new` to panic for negative values.
|
||||
To handle that, you need to add a special attribute to the test function.
|
||||
|
||||
You can refer to the docs:
|
||||
https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
|
||||
https://doc.rust-lang.org/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
|
||||
|
||||
# STANDARD LIBRARY TYPES
|
||||
|
||||
@@ -897,9 +890,9 @@ hint = """
|
||||
`capitalize_first`:
|
||||
|
||||
The variable `first` is a `char`. It needs to be capitalized and added to the
|
||||
remaining characters in `c` in order to return the correct `String`.
|
||||
remaining characters in `chars` in order to return the correct `String`.
|
||||
|
||||
The remaining characters in `c` can be viewed as a string slice using the
|
||||
The remaining characters in `chars` can be viewed as a string slice using the
|
||||
`as_str` method.
|
||||
|
||||
The documentation for `char` contains many useful methods.
|
||||
@@ -1014,7 +1007,7 @@ thread-local copy of the numbers.
|
||||
This is a simple exercise if you understand the underlying concepts, but if this
|
||||
is too much of a struggle, consider reading through all of Chapter 16 in The
|
||||
Book:
|
||||
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html"""
|
||||
https://doc.rust-lang.org/book/ch16-00-concurrency.html"""
|
||||
|
||||
[[exercises]]
|
||||
name = "cow1"
|
||||
@@ -1152,7 +1145,11 @@ test = false
|
||||
strict_clippy = true
|
||||
hint = """
|
||||
`for` loops over `Option` values are more clearly expressed as an `if-let`
|
||||
statement."""
|
||||
statement.
|
||||
|
||||
Not required to solve this exercise, but if you are interested in when iterating
|
||||
over `Option` can be useful, read the following section in the documentation:
|
||||
https://doc.rust-lang.org/std/option/#iterating-over-option"""
|
||||
|
||||
[[exercises]]
|
||||
name = "clippy3"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::needless_late_init)]
|
||||
|
||||
fn main() {
|
||||
// Reading uninitialized variables isn't allowed in Rust!
|
||||
// Therefore, we need to assign a value first.
|
||||
@@ -5,7 +7,7 @@ fn main() {
|
||||
|
||||
println!("Number {x}");
|
||||
|
||||
// It possible to declare a variable and initialize it later.
|
||||
// It is possible to declare a variable and initialize it later.
|
||||
// But it can't be used before initialization.
|
||||
let y: i32;
|
||||
y = 42;
|
||||
|
||||
@@ -7,15 +7,15 @@ mod tests {
|
||||
// TODO: Fix the compiler errors only by reordering the lines in the test.
|
||||
// Don't add, change or remove any line.
|
||||
#[test]
|
||||
fn move_semantics5() {
|
||||
let mut x = 100;
|
||||
fn move_semantics4() {
|
||||
let mut x = Vec::new();
|
||||
let y = &mut x;
|
||||
// `y` used here.
|
||||
*y += 100;
|
||||
y.push(42);
|
||||
// The mutable reference `y` is not used anymore,
|
||||
// therefore a new reference can be created.
|
||||
let z = &mut x;
|
||||
*z += 1000;
|
||||
assert_eq!(x, 1200);
|
||||
z.push(13);
|
||||
assert_eq!(x, [42, 13]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
get_char(&data);
|
||||
|
||||
string_uppercase(data);
|
||||
}
|
||||
#![allow(clippy::ptr_arg)]
|
||||
|
||||
// Borrows instead of taking ownership.
|
||||
// It is recommended to use `&str` instead of `&String` here. But this is
|
||||
@@ -19,3 +13,11 @@ fn string_uppercase(mut data: String) {
|
||||
|
||||
println!("{data}");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
get_char(&data);
|
||||
|
||||
string_uppercase(data);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
Quit,
|
||||
Echo,
|
||||
Resize,
|
||||
Move,
|
||||
Echo,
|
||||
ChangeColor,
|
||||
Quit,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", Message::Quit);
|
||||
println!("{:?}", Message::Echo);
|
||||
println!("{:?}", Message::Resize);
|
||||
println!("{:?}", Message::Move);
|
||||
println!("{:?}", Message::Echo);
|
||||
println!("{:?}", Message::ChangeColor);
|
||||
println!("{:?}", Message::Quit);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
Move { x: i64, y: i64 },
|
||||
Resize { width: u64, height: u64 },
|
||||
Move(Point),
|
||||
Echo(String),
|
||||
ChangeColor(u8, u8, u8),
|
||||
Quit,
|
||||
@@ -8,13 +17,17 @@ enum Message {
|
||||
|
||||
impl Message {
|
||||
fn call(&self) {
|
||||
println!("{:?}", self);
|
||||
println!("{self:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let messages = [
|
||||
Message::Move { x: 10, y: 30 },
|
||||
Message::Resize {
|
||||
width: 10,
|
||||
height: 30,
|
||||
},
|
||||
Message::Move(Point { x: 10, y: 15 }),
|
||||
Message::Echo(String::from("hello world")),
|
||||
Message::ChangeColor(200, 255, 255),
|
||||
Message::Quit,
|
||||
|
||||
@@ -1,44 +1,53 @@
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
enum Message {
|
||||
ChangeColor(u8, u8, u8),
|
||||
Echo(String),
|
||||
Resize { width: u64, height: u64 },
|
||||
Move(Point),
|
||||
Echo(String),
|
||||
ChangeColor(u8, u8, u8),
|
||||
Quit,
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x: u8,
|
||||
y: u8,
|
||||
}
|
||||
|
||||
struct State {
|
||||
color: (u8, u8, u8),
|
||||
width: u64,
|
||||
height: u64,
|
||||
position: Point,
|
||||
quit: bool,
|
||||
message: String,
|
||||
color: (u8, u8, u8),
|
||||
quit: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn change_color(&mut self, color: (u8, u8, u8)) {
|
||||
self.color = color;
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
self.quit = true;
|
||||
}
|
||||
|
||||
fn echo(&mut self, s: String) {
|
||||
self.message = s;
|
||||
fn resize(&mut self, width: u64, height: u64) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
}
|
||||
|
||||
fn move_position(&mut self, point: Point) {
|
||||
self.position = point;
|
||||
}
|
||||
|
||||
fn echo(&mut self, s: String) {
|
||||
self.message = s;
|
||||
}
|
||||
|
||||
fn change_color(&mut self, red: u8, green: u8, blue: u8) {
|
||||
self.color = (red, green, blue);
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
self.quit = true;
|
||||
}
|
||||
|
||||
fn process(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::ChangeColor(r, g, b) => self.change_color((r, g, b)),
|
||||
Message::Echo(s) => self.echo(s),
|
||||
Message::Resize { width, height } => self.resize(width, height),
|
||||
Message::Move(point) => self.move_position(point),
|
||||
Message::Echo(s) => self.echo(s),
|
||||
Message::ChangeColor(r, g, b) => self.change_color(r, g, b),
|
||||
Message::Quit => self.quit(),
|
||||
}
|
||||
}
|
||||
@@ -55,21 +64,29 @@ mod tests {
|
||||
#[test]
|
||||
fn test_match_message_call() {
|
||||
let mut state = State {
|
||||
quit: false,
|
||||
width: 0,
|
||||
height: 0,
|
||||
position: Point { x: 0, y: 0 },
|
||||
color: (0, 0, 0),
|
||||
message: String::from("hello world"),
|
||||
color: (0, 0, 0),
|
||||
quit: false,
|
||||
};
|
||||
|
||||
state.process(Message::ChangeColor(255, 0, 255));
|
||||
state.process(Message::Echo(String::from("Hello world!")));
|
||||
state.process(Message::Resize {
|
||||
width: 10,
|
||||
height: 30,
|
||||
});
|
||||
state.process(Message::Move(Point { x: 10, y: 15 }));
|
||||
state.process(Message::Echo(String::from("Hello world!")));
|
||||
state.process(Message::ChangeColor(255, 0, 255));
|
||||
state.process(Message::Quit);
|
||||
|
||||
assert_eq!(state.color, (255, 0, 255));
|
||||
assert_eq!(state.width, 10);
|
||||
assert_eq!(state.height, 30);
|
||||
assert_eq!(state.position.x, 10);
|
||||
assert_eq!(state.position.y, 15);
|
||||
assert!(state.quit);
|
||||
assert_eq!(state.message, "Hello world!");
|
||||
assert_eq!(state.color, (255, 0, 255));
|
||||
assert!(state.quit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
fn string_slice(arg: &str) {
|
||||
println!("{arg}");
|
||||
}
|
||||
|
||||
fn string(arg: String) {
|
||||
println!("{arg}");
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#[allow(dead_code)]
|
||||
mod delicious_snacks {
|
||||
// Added `pub` and used the expected alias after `as`.
|
||||
pub use self::fruits::PEAR as fruit;
|
||||
|
||||
@@ -28,13 +28,13 @@ fn build_scores_table(results: &str) -> HashMap<&str, Team> {
|
||||
let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();
|
||||
|
||||
// Insert the default with zeros if a team doesn't exist yet.
|
||||
let mut team_1 = scores.entry(team_1_name).or_insert_with(|| Team::default());
|
||||
let team_1 = scores.entry(team_1_name).or_insert_with(Team::default);
|
||||
// Update the values.
|
||||
team_1.goals_scored += team_1_score;
|
||||
team_1.goals_conceded += team_2_score;
|
||||
|
||||
// Similarely for the second team.
|
||||
let mut team_2 = scores.entry(team_2_name).or_insert_with(|| Team::default());
|
||||
let team_2 = scores.entry(team_2_name).or_insert_with(Team::default);
|
||||
team_2.goals_scored += team_2_score;
|
||||
team_2.goals_conceded += team_1_score;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// `hour_of_day` is higher than 23.
|
||||
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
|
||||
match hour_of_day {
|
||||
0..22 => Some(5),
|
||||
22..24 => Some(0),
|
||||
0..=21 => Some(5),
|
||||
22..=23 => Some(0),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ mod tests {
|
||||
fn raw_value() {
|
||||
// Using `unwrap` is fine in a test.
|
||||
let icecreams = maybe_icecream(12).unwrap();
|
||||
|
||||
assert_eq!(icecreams, 5);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,17 +5,18 @@
|
||||
// the player typed in the quantity, we get it as a string. They might have
|
||||
// typed anything, not just numbers!
|
||||
//
|
||||
// Right now, this function isn't handling the error case at all (and isn't
|
||||
// handling the success case properly either). What we want to do is: If we call
|
||||
// the `total_cost` function on a string that is not a number, that function
|
||||
// will return a `ParseIntError`. In that case, we want to immediately return
|
||||
// that error from our function and not try to multiply and add.
|
||||
// Right now, this function isn't handling the error case at all. What we want
|
||||
// to do is: If we call the `total_cost` function on a string that is not a
|
||||
// number, that function will return a `ParseIntError`. In that case, we want to
|
||||
// immediately return that error from our function and not try to multiply and
|
||||
// add.
|
||||
//
|
||||
// There are at least two ways to implement this that are both correct. But one
|
||||
// is a lot shorter!
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||
let processing_fee = 1;
|
||||
let cost_per_item = 5;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::comparison_chain)]
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
|
||||
@@ -36,7 +36,7 @@ impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<Self, CreationError> {
|
||||
match value {
|
||||
x if x < 0 => Err(CreationError::Negative),
|
||||
x if x == 0 => Err(CreationError::Zero),
|
||||
0 => Err(CreationError::Zero),
|
||||
x => Ok(Self(x as u64)),
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,6 @@ fn main() {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::num::IntErrorKind;
|
||||
|
||||
#[test]
|
||||
fn test_parse_error() {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
trait Licensed {
|
||||
fn licensing_info(&self) -> String {
|
||||
"Default license".to_string()
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum DivisionError {
|
||||
// Example: 42 / 0
|
||||
DivideByZero,
|
||||
// Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
|
||||
IntegerOverflow,
|
||||
// Example: 5 / 2 = 2.5
|
||||
NotDivisible,
|
||||
}
|
||||
|
||||
@@ -9,6 +13,10 @@ fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
|
||||
return Err(DivisionError::DivideByZero);
|
||||
}
|
||||
|
||||
if a == i64::MIN && b == -1 {
|
||||
return Err(DivisionError::IntegerOverflow);
|
||||
}
|
||||
|
||||
if a % b != 0 {
|
||||
return Err(DivisionError::NotDivisible);
|
||||
}
|
||||
@@ -51,6 +59,11 @@ mod tests {
|
||||
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integer_overflow() {
|
||||
assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_divisible() {
|
||||
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));
|
||||
|
||||
@@ -47,6 +47,23 @@ fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Pr
|
||||
.sum()
|
||||
}
|
||||
|
||||
// Equivalent to `count_collection_iterator` and `count_iterator`, iterating as
|
||||
// if the collection was a single container instead of a container of containers
|
||||
// (and more accurately, a single iterator instead of an iterator of iterators).
|
||||
fn count_collection_iterator_flat(
|
||||
collection: &[HashMap<String, Progress>],
|
||||
value: Progress,
|
||||
) -> usize {
|
||||
// `collection` is a slice of hash maps.
|
||||
// collection = [{ "variables1": Complete, "from_str": None, … },
|
||||
// { "variables2": Complete, … }, … ]
|
||||
collection
|
||||
.iter()
|
||||
.flat_map(HashMap::values) // or just `.flatten()` when wanting the default iterator (`HashMap::iter`)
|
||||
.filter(|val| **val == value)
|
||||
.count()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// You can optionally experiment here.
|
||||
}
|
||||
@@ -54,10 +71,9 @@ fn main() {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use Progress::*;
|
||||
|
||||
fn get_map() -> HashMap<String, Progress> {
|
||||
use Progress::*;
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(String::from("variables1"), Complete);
|
||||
map.insert(String::from("functions1"), Complete);
|
||||
@@ -70,8 +86,6 @@ mod tests {
|
||||
}
|
||||
|
||||
fn get_vec_map() -> Vec<HashMap<String, Progress>> {
|
||||
use Progress::*;
|
||||
|
||||
let map = get_map();
|
||||
|
||||
let mut other = HashMap::new();
|
||||
@@ -87,25 +101,25 @@ mod tests {
|
||||
#[test]
|
||||
fn count_complete() {
|
||||
let map = get_map();
|
||||
assert_eq!(count_iterator(&map, Progress::Complete), 3);
|
||||
assert_eq!(count_iterator(&map, Complete), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_some() {
|
||||
let map = get_map();
|
||||
assert_eq!(count_iterator(&map, Progress::Some), 1);
|
||||
assert_eq!(count_iterator(&map, Some), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_none() {
|
||||
let map = get_map();
|
||||
assert_eq!(count_iterator(&map, Progress::None), 2);
|
||||
assert_eq!(count_iterator(&map, None), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_complete_equals_for() {
|
||||
let map = get_map();
|
||||
let progress_states = [Progress::Complete, Progress::Some, Progress::None];
|
||||
let progress_states = [Complete, Some, None];
|
||||
for progress_state in progress_states {
|
||||
assert_eq!(
|
||||
count_for(&map, progress_state),
|
||||
@@ -117,34 +131,38 @@ mod tests {
|
||||
#[test]
|
||||
fn count_collection_complete() {
|
||||
let collection = get_vec_map();
|
||||
assert_eq!(
|
||||
count_collection_iterator(&collection, Progress::Complete),
|
||||
6,
|
||||
);
|
||||
assert_eq!(count_collection_iterator(&collection, Complete), 6);
|
||||
assert_eq!(count_collection_iterator_flat(&collection, Complete), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_collection_some() {
|
||||
let collection = get_vec_map();
|
||||
assert_eq!(count_collection_iterator(&collection, Progress::Some), 1);
|
||||
assert_eq!(count_collection_iterator(&collection, Some), 1);
|
||||
assert_eq!(count_collection_iterator_flat(&collection, Some), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_collection_none() {
|
||||
let collection = get_vec_map();
|
||||
assert_eq!(count_collection_iterator(&collection, Progress::None), 4);
|
||||
assert_eq!(count_collection_iterator(&collection, None), 4);
|
||||
assert_eq!(count_collection_iterator_flat(&collection, None), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_collection_equals_for() {
|
||||
let collection = get_vec_map();
|
||||
let progress_states = [Progress::Complete, Progress::Some, Progress::None];
|
||||
let progress_states = [Complete, Some, None];
|
||||
|
||||
for progress_state in progress_states {
|
||||
assert_eq!(
|
||||
count_collection_for(&collection, progress_state),
|
||||
count_collection_iterator(&collection, progress_state),
|
||||
);
|
||||
assert_eq!(
|
||||
count_collection_for(&collection, progress_state),
|
||||
count_collection_iterator_flat(&collection, progress_state),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// In this exercise, we are given a `Vec` of u32 called `numbers` with values
|
||||
// In this exercise, we are given a `Vec` of `u32` called `numbers` with values
|
||||
// ranging from 0 to 99. We would like to use this set of numbers within 8
|
||||
// different threads simultaneously. Each thread is going to get the sum of
|
||||
// every eighth value with an offset.
|
||||
@@ -9,8 +9,11 @@
|
||||
// …
|
||||
// The eighth thread (offset 7), will sum 7, 15, 23, …
|
||||
//
|
||||
// Because we are using threads, our values need to be thread-safe. Therefore,
|
||||
// we are using `Arc`.
|
||||
// Each thread should own a reference-counting pointer to the vector of
|
||||
// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`.
|
||||
//
|
||||
// Don't get distracted by how threads are spawned and joined. We will practice
|
||||
// that later in the exercises about threads.
|
||||
|
||||
// Don't change the lines below.
|
||||
#![forbid(unused_imports)]
|
||||
|
||||
@@ -45,8 +45,9 @@ mod tests {
|
||||
#[test]
|
||||
fn owned_no_mutation() {
|
||||
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
|
||||
// case, no mutation occurs and thus also no clone. But the result is
|
||||
// still owned because it was never borrowed or mutated.
|
||||
// case, no mutation occurs (all numbers are already absolute) and thus
|
||||
// also no clone. But the result is still owned because it was never
|
||||
// borrowed or mutated.
|
||||
let vec = vec![0, 1, 2];
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
@@ -56,9 +57,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn owned_mutation() {
|
||||
// Of course this is also the case if a mutation does occur. In this
|
||||
// case, the call to `to_mut()` in the `abs_all` function returns a
|
||||
// reference to the same data as before.
|
||||
// Of course this is also the case if a mutation does occur (not all
|
||||
// numbers are absolute). In this case, the call to `to_mut()` in the
|
||||
// `abs_all` function returns a reference to the same data as before.
|
||||
let vec = vec![-1, 0, 1];
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::rc::Rc;
|
||||
#[derive(Debug)]
|
||||
struct Sun;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
enum Planet {
|
||||
Mercury(Rc<Sun>),
|
||||
|
||||
@@ -15,7 +15,7 @@ fn char_counter<T: AsRef<str>>(arg: T) -> usize {
|
||||
// Squares a number using `as_mut()`.
|
||||
fn num_sq<T: AsMut<u32>>(arg: &mut T) {
|
||||
let arg = arg.as_mut();
|
||||
*arg = *arg * *arg;
|
||||
*arg *= *arg;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// type itself. You can read more about it in the documentation:
|
||||
// https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
||||
|
||||
#![allow(clippy::useless_vec)]
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Mary is buying apples. The price of an apple is calculated as follows:
|
||||
// - An apple costs 2 rustbucks.
|
||||
// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck!
|
||||
// Write a function that calculates the price of an order of apples given the
|
||||
// quantity bought.
|
||||
// - However, if Mary buys more than 40 apples, the price of each apple in the
|
||||
// entire order is reduced to only 1 rustbuck!
|
||||
|
||||
fn calculate_price_of_apples(n_apples: u64) -> u64 {
|
||||
if n_apples > 40 {
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
// This is a quiz for the following sections:
|
||||
// - Strings
|
||||
// - Vecs
|
||||
// - Move semantics
|
||||
// - Modules
|
||||
// - Enums
|
||||
//
|
||||
// Let's build a little machine in the form of a function. As input, we're going
|
||||
// to give a list of strings and commands. These commands determine what action
|
||||
// is going to be applied to the string. It can either be:
|
||||
@@ -13,7 +6,7 @@
|
||||
// - Append "bar" to the string a specified amount of times
|
||||
//
|
||||
// The exact form of this will be:
|
||||
// - The input is going to be a vector of a 2-length tuple,
|
||||
// - The input is going to be a vector of 2-length tuples,
|
||||
// the first element is the string, the second one is the command.
|
||||
// - The output element is going to be a vector of strings.
|
||||
|
||||
@@ -31,17 +24,12 @@ mod my_module {
|
||||
pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {
|
||||
let mut output = Vec::new();
|
||||
|
||||
for (mut string, command) in input {
|
||||
for (string, command) in input {
|
||||
// Create the new string.
|
||||
let new_string = match command {
|
||||
Command::Uppercase => string.to_uppercase(),
|
||||
Command::Trim => string.trim().to_string(),
|
||||
Command::Append(n) => {
|
||||
for _ in 0..n {
|
||||
string += "bar";
|
||||
}
|
||||
string
|
||||
}
|
||||
Command::Append(n) => string + &"bar".repeat(n),
|
||||
};
|
||||
|
||||
// Push the new string to the output vector.
|
||||
@@ -56,15 +44,10 @@ mod my_module {
|
||||
pub fn transformer_iter(input: Vec<(String, Command)>) -> Vec<String> {
|
||||
input
|
||||
.into_iter()
|
||||
.map(|(mut string, command)| match command {
|
||||
.map(|(string, command)| match command {
|
||||
Command::Uppercase => string.to_uppercase(),
|
||||
Command::Trim => string.trim().to_string(),
|
||||
Command::Append(n) => {
|
||||
for _ in 0..n {
|
||||
string += "bar";
|
||||
}
|
||||
string
|
||||
}
|
||||
Command::Append(n) => string + &"bar".repeat(n),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// This quiz tests:
|
||||
// - Generics
|
||||
// - Traits
|
||||
//
|
||||
// An imaginary magical school has a new report card generation system written
|
||||
// in Rust! Currently, the system only supports creating report cards where the
|
||||
// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the
|
||||
|
||||
@@ -403,6 +403,9 @@ impl AppState {
|
||||
writeln!(writer, "{}", "ok".green())?;
|
||||
}
|
||||
|
||||
// Write that the last exercise is done.
|
||||
self.write()?;
|
||||
|
||||
clear_terminal(writer)?;
|
||||
writer.write_all(FENISH_LINE.as_bytes())?;
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ mod tests {
|
||||
test: true,
|
||||
strict_clippy: true,
|
||||
hint: String::new(),
|
||||
skip_check_unsolved: false,
|
||||
},
|
||||
ExerciseInfo {
|
||||
name: String::from("2"),
|
||||
@@ -118,6 +119,7 @@ mod tests {
|
||||
test: false,
|
||||
strict_clippy: false,
|
||||
hint: String::new(),
|
||||
skip_check_unsolved: false,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -92,6 +92,10 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<hashbrown::HashSet<
|
||||
bail!("The `main` function is missing in the file `{path}`.\nCreate at least an empty `main` function to avoid language server errors");
|
||||
}
|
||||
|
||||
if !file_buf.contains("// TODO") {
|
||||
bail!("Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user.");
|
||||
}
|
||||
|
||||
if !exercise_info.test && file_buf.contains("#[test]") {
|
||||
bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file");
|
||||
}
|
||||
@@ -158,7 +162,46 @@ fn check_unexpected_files(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_exercises(info_file: &InfoFile) -> Result<()> {
|
||||
fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> Result<()> {
|
||||
let error_occurred = AtomicBool::new(false);
|
||||
|
||||
println!(
|
||||
"Running all exercises to check that they aren't already solved. This may take a while…\n",
|
||||
);
|
||||
thread::scope(|s| {
|
||||
for exercise_info in &info_file.exercises {
|
||||
if exercise_info.skip_check_unsolved {
|
||||
continue;
|
||||
}
|
||||
|
||||
s.spawn(|| {
|
||||
let error = |e| {
|
||||
let mut stderr = io::stderr().lock();
|
||||
stderr.write_all(e).unwrap();
|
||||
stderr.write_all(b"\nProblem with the exercise ").unwrap();
|
||||
stderr.write_all(exercise_info.name.as_bytes()).unwrap();
|
||||
stderr.write_all(SEPARATOR).unwrap();
|
||||
error_occurred.store(true, atomic::Ordering::Relaxed);
|
||||
};
|
||||
|
||||
let mut output = Vec::with_capacity(OUTPUT_CAPACITY);
|
||||
match exercise_info.run_exercise(&mut output, target_dir) {
|
||||
Ok(true) => error(b"Already solved!"),
|
||||
Ok(false) => (),
|
||||
Err(e) => error(e.to_string().as_bytes()),
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if error_occurred.load(atomic::Ordering::Relaxed) {
|
||||
bail!(CHECK_EXERCISES_UNSOLVED_ERR);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_exercises(info_file: &InfoFile, target_dir: &Path) -> Result<()> {
|
||||
match info_file.format_version.cmp(&CURRENT_FORMAT_VERSION) {
|
||||
Ordering::Less => bail!("`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\nPlease migrate to the latest format version"),
|
||||
Ordering::Greater => bail!("`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\nTry updating the Rustlings program"),
|
||||
@@ -168,15 +211,14 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> {
|
||||
let info_file_paths = check_info_file_exercises(info_file)?;
|
||||
check_unexpected_files("exercises", &info_file_paths)?;
|
||||
|
||||
Ok(())
|
||||
check_exercises_unsolved(info_file, target_dir)
|
||||
}
|
||||
|
||||
fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> {
|
||||
let target_dir = parse_target_dir()?;
|
||||
fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &Path) -> Result<()> {
|
||||
let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len()));
|
||||
let error_occurred = AtomicBool::new(false);
|
||||
|
||||
println!("Running all solutions. This may take a while...\n");
|
||||
println!("Running all solutions. This may take a while…\n");
|
||||
thread::scope(|s| {
|
||||
for exercise_info in &info_file.exercises {
|
||||
s.spawn(|| {
|
||||
@@ -202,7 +244,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()>
|
||||
}
|
||||
|
||||
let mut output = Vec::with_capacity(OUTPUT_CAPACITY);
|
||||
match exercise_info.run_solution(&mut output, &target_dir) {
|
||||
match exercise_info.run_solution(&mut output, target_dir) {
|
||||
Ok(true) => {
|
||||
paths.lock().unwrap().insert(PathBuf::from(path));
|
||||
}
|
||||
@@ -238,8 +280,9 @@ pub fn check(require_solutions: bool) -> Result<()> {
|
||||
check_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"")?;
|
||||
}
|
||||
|
||||
check_exercises(&info_file)?;
|
||||
check_solutions(require_solutions, &info_file)?;
|
||||
let target_dir = parse_target_dir()?;
|
||||
check_exercises(&info_file, &target_dir)?;
|
||||
check_solutions(require_solutions, &info_file, &target_dir)?;
|
||||
|
||||
println!("\nEverything looks fine!");
|
||||
|
||||
@@ -248,3 +291,6 @@ pub fn check(require_solutions: bool) -> Result<()> {
|
||||
|
||||
const SEPARATOR: &[u8] =
|
||||
b"\n========================================================================================\n";
|
||||
|
||||
const CHECK_EXERCISES_UNSOLVED_ERR: &str = "At least one exercise is already solved or failed to run. See the output above.
|
||||
If this is an intro exercise that is intended to be already solved, add `skip_check_unsolved = true` to the exercise's metadata in the `info.toml` file.";
|
||||
|
||||
@@ -139,7 +139,7 @@ const README: &str = "# Rustlings 🦀
|
||||
|
||||
Welcome to these third-party Rustlings exercises 😃
|
||||
|
||||
First, [install Rustlings using the official instructions in the README of the Rustlings project](https://github.com/rust-lang/rustlings) ✅
|
||||
First, [install Rustlings using the official instructions](https://github.com/rust-lang/rustlings) ✅
|
||||
|
||||
Then, open your terminal in this directory and run `rustlings` to get started with the exercises 🚀
|
||||
Then, clone this repository, open a terminal in this directory and run `rustlings` to get started with the exercises 🚀
|
||||
";
|
||||
|
||||
@@ -11,14 +11,17 @@ pub struct ExerciseInfo {
|
||||
pub name: String,
|
||||
/// Exercise's directory name inside the `exercises/` directory.
|
||||
pub dir: Option<String>,
|
||||
#[serde(default = "default_true")]
|
||||
/// Run `cargo test` on the exercise.
|
||||
#[serde(default = "default_true")]
|
||||
pub test: bool,
|
||||
/// Deny all Clippy warnings.
|
||||
#[serde(default)]
|
||||
pub strict_clippy: bool,
|
||||
/// The exercise's hint to be shown to the user on request.
|
||||
pub hint: String,
|
||||
/// The exercise is already solved. Ignore it when checking that all exercises are unsolved.
|
||||
#[serde(default)]
|
||||
pub skip_check_unsolved: bool,
|
||||
}
|
||||
#[inline(always)]
|
||||
const fn default_true() -> bool {
|
||||
|
||||
13
src/main.rs
13
src/main.rs
@@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result};
|
||||
use app_state::StateFileStatus;
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::{
|
||||
io::{self, BufRead, StdoutLock, Write},
|
||||
io::{self, BufRead, IsTerminal, StdoutLock, Write},
|
||||
path::Path,
|
||||
process::exit,
|
||||
};
|
||||
@@ -148,6 +148,10 @@ fn main() -> Result<()> {
|
||||
|
||||
match args.command {
|
||||
None => {
|
||||
if !io::stdout().is_terminal() {
|
||||
bail!("Unsupported or missing terminal/TTY");
|
||||
}
|
||||
|
||||
let notify_exercise_names = if args.manual_run {
|
||||
None
|
||||
} else {
|
||||
@@ -197,9 +201,10 @@ fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const OLD_METHOD_ERR: &str = "You are trying to run Rustlings using the old method before v6.
|
||||
const OLD_METHOD_ERR: &str =
|
||||
"You are trying to run Rustlings using the old method before version 6.
|
||||
The new method doesn't include cloning the Rustlings' repository.
|
||||
Please follow the instructions in the README:
|
||||
Please follow the instructions in `README.md`:
|
||||
https://github.com/rust-lang/rustlings#getting-started";
|
||||
|
||||
const FORMAT_VERSION_HIGHER_ERR: &str =
|
||||
@@ -216,5 +221,5 @@ const PRE_INIT_MSG: &str = r"
|
||||
|_| \__,_|___/\__|_|_|_| |_|\__, |___/
|
||||
|___/
|
||||
|
||||
The `exercises` directory wasn't found in the current directory.
|
||||
The `exercises/` directory couldn't be found in the current directory.
|
||||
If you are just starting with Rustlings, run the command `rustlings init` to initialize it.";
|
||||
|
||||
Reference in New Issue
Block a user