Compare commits

...

52 Commits

Author SHA1 Message Date
liv
b2b3005670 feat: add async exercises 2025-05-16 15:53:31 +02:00
mo8it
e73fff3bd4 Add dev alias 2025-05-16 11:09:06 +02:00
mo8it
8dff0df266 Use std pipe 2025-05-16 11:09:06 +02:00
liv
5ee7dfb5c2 chore: build site with proper path prefix 2025-05-16 11:05:02 +02:00
mo8it
9a3586878d Sync solution 2025-05-13 16:24:42 +02:00
Mo Bitar
a99433c62d Merge pull request #2215 from Rudxain/strim
test idempotence of `trim_me` in `strings3.rs`
2025-05-13 16:23:35 +02:00
mo8it
e76ca5e2b9 Use a separate target dir for rust analyzer 2025-05-12 20:38:04 +02:00
mo8it
48bab77609 Apply Clippy lints 2025-05-12 20:31:13 +02:00
mo8it
a063bcfb4c Update deps 2025-05-12 20:30:56 +02:00
mo8it
c5f49cfa48 Remove needless_option_as_deref exception 2025-05-12 20:30:51 +02:00
mo8it
9bcd4198c5 Fix formatting 2025-04-29 21:36:56 +02:00
mo8it
29dc8ea9fa Update deps 2025-04-29 21:35:58 +02:00
Mo Bitar
fa91814aa9 Merge pull request #2232 from ethdew19/main
Fix typo in traits hint
2025-04-29 21:34:47 +02:00
Mo Bitar
0b91db2195 Merge pull request #2236 from rahmatnazali/ice_cream
Fix possible typo of `icecream` to `ice cream` on `options1.rs`
2025-04-29 21:34:21 +02:00
Rahmat Nazali Salimi
7b2d42b0f0 Change icecream to ice cream 2025-04-10 15:38:43 +07:00
Ethan
bd3bdd620b Fix typo in traits hint 2025-04-08 20:23:11 -05:00
Mo Bitar
8b4562e102 Merge pull request #2219 from ubitux/no-todo
Remove TODO from 2 solutions
2025-04-03 19:36:06 +02:00
mo8it
63d8986f2a Update links 2025-04-03 18:22:55 +02:00
mo8it
ecaecc2f76 Update deps 2025-04-03 17:58:36 +02:00
Mo Bitar
78194b4441 Merge pull request #2224 from cassian-goode/patch-1
Fix typo - errors5.rs
2025-04-03 17:52:57 +02:00
Mo Bitar
44699e9b1b Merge pull request #2227 from Hunter-Plus/patch-1
Update README.md
2025-04-03 17:52:15 +02:00
Hunter Z
9978c17d5f Update README.md
Update the URL while add more reference.
2025-03-31 12:58:06 +08:00
cassian-goode
3cc7e0377c Fix typo - errors5.rs
Minor typo correction in exercise instructions
2025-03-25 09:24:49 -04:00
Clément Bœsch
d2abc359cc Remove TODO from 2 solutions 2025-03-17 18:36:13 +01:00
mo8it
7c0d269279 Update README 2025-03-14 11:42:16 +01:00
mo8it
8db85946af Update deps 2025-03-14 11:33:56 +01:00
mo8it
7019f4d178 Update pipeline 2025-03-14 11:33:56 +01:00
Ricardo Fernández Serrata
fcd77a83cc test trim idempotence 2025-03-07 19:17:11 -04:00
Mo Bitar
ae444eb3da Merge pull request #2213 from peterneave/main
Use consistent apostrophes in markdown files
2025-03-02 17:30:29 +01:00
Peter Neave
425c9821e0 Use consistent apostrophes in markdown files 2025-02-28 11:46:39 +11:00
mo8it
46c6fb2c82 Update deps 2025-02-25 11:21:19 +01:00
mo8it
374c3874af Apply 2024 edition formatting to solutions 2025-02-21 13:08:34 +01:00
mo8it
1eb6c1e469 Update the edition of the solution format checker 2025-02-21 13:06:11 +01:00
mo8it
06af3ffc99 Bump MSRV in release hook 2025-02-18 20:17:27 +01:00
mo8it
65dc019fa6 Fix new Clippy error in solution 2025-02-18 20:15:50 +01:00
mo8it
a56ccb6f4f Fix new Clippy lint 2025-02-18 20:12:23 +01:00
mo8it
d9872f2615 Upgrade to edition 2024 2025-02-18 20:10:52 +01:00
mo8it
298be671b9 Update deps 2025-02-18 20:03:49 +01:00
mo8it
fbfd4f25e7 Disable following symlinks in the watcher 2025-01-16 10:41:48 +01:00
mo8it
d12735a573 Update deps 2025-01-16 10:41:17 +01:00
mo8it
1aec7c1152 Fix Windows CI 2025-01-01 22:07:41 +01:00
mo8it
0b55809bb9 Fix building from source on Windows 2025-01-01 22:01:39 +01:00
mo8it
bde6f7470c Co-ordinates -> Coordinates 2024-12-28 16:46:24 +01:00
mo8it
53ec59ed95 Rename translations 2024-12-28 16:41:43 +01:00
mo8it
ed1ee38923 Link to simplified Chinese translation 2024-12-28 16:40:07 +01:00
Mo
26cf4989a2 Merge pull request #2173 from JoelMarcey/if2-comment-fix
Some checks failed
Rustlings Tests / clippy (push) Failing after 6s
Rustlings Tests / fmt (push) Failing after 9s
Rustlings Tests / test (ubuntu-latest) (push) Failing after 24s
Rustlings Tests / dev-check (push) Failing after 5s
Web / Build and deploy site and docs (push) Failing after 56s
Rustlings Tests / test (macOS-latest) (push) Has been cancelled
Rustlings Tests / test (windows-latest) (push) Has been cancelled
Fix argument comment in test of if2.rs
2024-12-13 19:48:16 +01:00
Joel Marcey
6e60f441e9 Fix argument comment in test of if2.rs 2024-12-13 10:44:21 -08:00
mo8it
d07de879a7 Update deps 2024-12-11 00:12:49 +01:00
Mo
dd0634c483 Merge pull request #2158 from mnshdw/mnshdw/feedback-errors6
errors6: Add alternative solution using From trait
2024-11-14 14:49:57 +01:00
Antoine Dupuis
fc0cd8f0f8 Switch comment style to // 2024-11-14 09:14:40 +01:00
Antoine Dupuis
d5cae8ff59 Add alternative solution using From trait 2024-11-13 23:51:09 +01:00
mo8it
38016cb2d6 clippy3: Make the intent more clear 2024-11-13 16:06:41 +01:00
86 changed files with 647 additions and 334 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[alias]
dev = ["run", "--", "dev"]

View File

@@ -22,22 +22,22 @@ jobs:
- uses: DavidAnson/markdownlint-cli2-action@v16
with:
globs: "exercises/**/*.md"
- name: Run cargo fmt
- name: rustfmt
run: cargo fmt --all --check
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: swatinem/rust-cache@v2
- name: Run cargo test
- name: cargo test
run: cargo test --workspace
dev-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: swatinem/rust-cache@v2
- name: Run rustlings dev check
- name: rustlings dev check
run: cargo run -- dev check --require-solutions

View File

@@ -1,3 +1,10 @@
## Unreleased
### Changed
- Upgrade to Rust edition 2024
- Raise the minimum supported Rust version to `1.87`
<a name="6.4.0"></a>
## 6.4.0 (2024-11-11)

273
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "anstream"
@@ -43,19 +43,20 @@ dependencies = [
[[package]]
name = "anstyle-wincon"
version = "3.0.6"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
"once_cell",
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.93"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "autocfg"
@@ -71,9 +72,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.6.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "cfg-if"
@@ -83,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.20"
version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
dependencies = [
"clap_builder",
"clap_derive",
@@ -93,9 +94,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.20"
version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
dependencies = [
"anstream",
"anstyle",
@@ -105,9 +106,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.18"
version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
dependencies = [
"heck",
"proc-macro2",
@@ -117,9 +118,9 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.7.2"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "colorchoice"
@@ -129,12 +130,13 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "crossterm"
version = "0.28.1"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.1",
"crossterm_winapi",
"document-features",
"mio",
"parking_lot",
"rustix",
@@ -153,26 +155,35 @@ dependencies = [
]
[[package]]
name = "equivalent"
version = "1.0.1"
name = "document-features"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
dependencies = [
"litrs",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.9"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
name = "fastrand"
version = "2.2.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "filetime"
@@ -196,10 +207,22 @@ dependencies = [
]
[[package]]
name = "hashbrown"
version = "0.15.1"
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
]
[[package]]
name = "hashbrown"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
[[package]]
name = "heck"
@@ -207,17 +230,11 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "indexmap"
version = "2.6.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
@@ -225,11 +242,11 @@ dependencies = [
[[package]]
name = "inotify"
version = "0.10.2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc"
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
dependencies = [
"bitflags 1.3.2",
"bitflags 2.9.1",
"inotify-sys",
"libc",
]
@@ -243,15 +260,6 @@ dependencies = [
"libc",
]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
@@ -260,15 +268,15 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.11"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "kqueue"
version = "1.0.8"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
dependencies = [
"kqueue-sys",
"libc",
@@ -286,9 +294,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.162"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libredox"
@@ -296,16 +304,22 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.1",
"libc",
"redox_syscall",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]]
name = "lock_api"
@@ -319,9 +333,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.22"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "memchr"
@@ -331,24 +345,23 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mio"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"hermit-abi",
"libc",
"log",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
[[package]]
name = "notify"
version = "7.0.0"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009"
checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.1",
"filetime",
"fsevent-sys",
"inotify",
@@ -358,33 +371,20 @@ dependencies = [
"mio",
"notify-types",
"walkdir",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
name = "notify-types"
version = "1.0.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df"
dependencies = [
"instant",
]
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
[[package]]
name = "once_cell"
version = "1.20.2"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "os_pipe"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "parking_lot"
@@ -411,42 +411,48 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.89"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.7"
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "redox_syscall"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.1",
]
[[package]]
name = "rustix"
version = "0.38.40"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags 2.6.0",
"bitflags 2.9.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -457,7 +463,6 @@ dependencies = [
"clap",
"crossterm",
"notify",
"os_pipe",
"rustix",
"rustlings-macros",
"serde",
@@ -477,9 +482,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.18"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
@@ -498,18 +503,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.214"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.214"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
@@ -518,9 +523,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.132"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
@@ -539,9 +544,9 @@ dependencies = [
[[package]]
name = "signal-hook"
version = "0.3.17"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
@@ -560,18 +565,18 @@ dependencies = [
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.13.2"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "strsim"
@@ -581,9 +586,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.87"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
@@ -592,12 +597,12 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.14.0"
version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [
"cfg-if",
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys 0.59.0",
@@ -605,18 +610,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.8"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.22"
version = "0.22.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
dependencies = [
"indexmap",
"serde",
@@ -627,9 +632,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.13"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "utf8parse"
@@ -653,6 +658,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "winapi"
version = "0.3.9"
@@ -768,9 +782,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.20"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.1",
]

View File

@@ -15,12 +15,12 @@ authors = [
]
repository = "https://github.com/rust-lang/rustlings"
license = "MIT"
edition = "2021" # On Update: Update the edition of the `rustfmt` command that checks the solutions.
rust-version = "1.80"
edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and `CARGO_TOML` in `dev new`.
rust-version = "1.87"
[workspace.dependencies]
serde = { version = "1.0.214", features = ["derive"] }
toml_edit = { version = "0.22.22", default-features = false, features = ["parse", "serde"] }
serde = { version = "1.0", features = ["derive"] }
toml_edit = { version = "0.22", default-features = false, features = ["parse", "serde"] }
[package]
name = "rustlings"
@@ -46,21 +46,20 @@ include = [
]
[dependencies]
anyhow = "1.0.93"
clap = { version = "4.5.20", features = ["derive"] }
crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] }
notify = "7.0.0"
os_pipe = "1.2.1"
anyhow = "1.0"
clap = { version = "4.5", features = ["derive"] }
crossterm = { version = "0.29", default-features = false, features = ["windows", "events"] }
notify = "8.0"
rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" }
serde_json = "1.0.132"
serde_json = "1.0"
serde.workspace = true
toml_edit.workspace = true
[target.'cfg(not(windows))'.dependencies]
rustix = { version = "0.38.38", default-features = false, features = ["std", "stdio", "termios"] }
rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] }
[dev-dependencies]
tempfile = "3.14.0"
tempfile = "3.19"
[profile.release]
panic = "abort"
@@ -84,8 +83,6 @@ infinite_loop = "deny"
mem_forget = "deny"
dbg_macro = "warn"
todo = "warn"
# TODO: Remove after the following fix is released: https://github.com/rust-lang/rust-clippy/pull/13102
needless_option_as_deref = "allow"
[lints]
workspace = true

View File

@@ -21,12 +21,13 @@ Before installing Rustlings, you need to have the **latest version of Rust** ins
Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust.
This will also install _Cargo_, Rust's package/project manager.
> 🐧 If you're on Linux, make sure you've installed `gcc` (for a linker).
> 🐧 If you are on Linux, make sure you have installed `gcc` (for a linker).
>
> Deb: `sudo apt install gcc`.
> Dnf: `sudo dnf install gcc`.
> Deb: `sudo apt install gcc`
>
> Dnf: `sudo dnf install gcc`
> 🍎 If you're on MacOS, make sure you've installed Xcode and its developer tools by running `xcode-select --install`.
> 🍎 If you are on MacOS, make sure you have installed Xcode and its developer tools by running `xcode-select --install`.
### Installing Rustlings
@@ -102,7 +103,7 @@ Ask for hints by entering `h` in the _watch mode_ 💡
### Watch Mode
After [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`.
After the [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`.
This will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers).
It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory.
@@ -137,7 +138,8 @@ If you need any help while doing the exercises and the builtin-hints aren't help
Third-party exercises are a set of exercises maintained by the community.
You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them:
- [日本語版 Rustlings](https://github.com/sotanengel/rustlings-jp)A Japanese translation of the Rustlings exercises.
- 🇯🇵 [Japanese Rustlings](https://github.com/sotanengel/rustlings-jp)A Japanese translation of the Rustlings exercises.
- 🇨🇳 [Simplified Chinese Rustlings](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises.
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?
@@ -160,6 +162,4 @@ cargo uninstall rustlings
See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md) 🔗
## Contributors ✨
Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) 🎉
Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) ✨

5
build.rs Normal file
View File

@@ -0,0 +1,5 @@
fn main() {
// Fix building from source on Windows because it can't handle file links.
#[cfg(windows)]
let _ = std::fs::copy("dev/Cargo.toml", "dev-Cargo.toml");
}

View File

@@ -1,4 +1,4 @@
# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update`. This comment line will be stripped in `rustlings init`.
# Don't edit the `bin` list manually! It is updated by `cargo dev update`. This comment line will be stripped in `rustlings init`.
bin = [
{ name = "intro1", path = "../exercises/00_intro/intro1.rs" },
{ name = "intro1_sol", path = "../solutions/00_intro/intro1.rs" },
@@ -164,38 +164,45 @@ bin = [
{ name = "threads2_sol", path = "../solutions/20_threads/threads2.rs" },
{ name = "threads3", path = "../exercises/20_threads/threads3.rs" },
{ name = "threads3_sol", path = "../solutions/20_threads/threads3.rs" },
{ name = "macros1", path = "../exercises/21_macros/macros1.rs" },
{ name = "macros1_sol", path = "../solutions/21_macros/macros1.rs" },
{ name = "macros2", path = "../exercises/21_macros/macros2.rs" },
{ name = "macros2_sol", path = "../solutions/21_macros/macros2.rs" },
{ name = "macros3", path = "../exercises/21_macros/macros3.rs" },
{ name = "macros3_sol", path = "../solutions/21_macros/macros3.rs" },
{ name = "macros4", path = "../exercises/21_macros/macros4.rs" },
{ name = "macros4_sol", path = "../solutions/21_macros/macros4.rs" },
{ name = "clippy1", path = "../exercises/22_clippy/clippy1.rs" },
{ name = "clippy1_sol", path = "../solutions/22_clippy/clippy1.rs" },
{ name = "clippy2", path = "../exercises/22_clippy/clippy2.rs" },
{ name = "clippy2_sol", path = "../solutions/22_clippy/clippy2.rs" },
{ name = "clippy3", path = "../exercises/22_clippy/clippy3.rs" },
{ name = "clippy3_sol", path = "../solutions/22_clippy/clippy3.rs" },
{ name = "using_as", path = "../exercises/23_conversions/using_as.rs" },
{ name = "using_as_sol", path = "../solutions/23_conversions/using_as.rs" },
{ name = "from_into", path = "../exercises/23_conversions/from_into.rs" },
{ name = "from_into_sol", path = "../solutions/23_conversions/from_into.rs" },
{ name = "from_str", path = "../exercises/23_conversions/from_str.rs" },
{ name = "from_str_sol", path = "../solutions/23_conversions/from_str.rs" },
{ name = "try_from_into", path = "../exercises/23_conversions/try_from_into.rs" },
{ name = "try_from_into_sol", path = "../solutions/23_conversions/try_from_into.rs" },
{ name = "as_ref_mut", path = "../exercises/23_conversions/as_ref_mut.rs" },
{ name = "as_ref_mut_sol", path = "../solutions/23_conversions/as_ref_mut.rs" },
{ name = "async1", path = "../exercises/21_async/async1.rs" },
{ name = "async1_sol", path = "../solutions/21_async/async1.rs" },
{ name = "async2", path = "../exercises/21_async/async2.rs" },
{ name = "async2_sol", path = "../solutions/21_async/async2.rs" },
{ name = "macros1", path = "../exercises/22_macros/macros1.rs" },
{ name = "macros1_sol", path = "../solutions/22_macros/macros1.rs" },
{ name = "macros2", path = "../exercises/22_macros/macros2.rs" },
{ name = "macros2_sol", path = "../solutions/22_macros/macros2.rs" },
{ name = "macros3", path = "../exercises/22_macros/macros3.rs" },
{ name = "macros3_sol", path = "../solutions/22_macros/macros3.rs" },
{ name = "macros4", path = "../exercises/22_macros/macros4.rs" },
{ name = "macros4_sol", path = "../solutions/22_macros/macros4.rs" },
{ name = "clippy1", path = "../exercises/23_clippy/clippy1.rs" },
{ name = "clippy1_sol", path = "../solutions/23_clippy/clippy1.rs" },
{ name = "clippy2", path = "../exercises/23_clippy/clippy2.rs" },
{ name = "clippy2_sol", path = "../solutions/23_clippy/clippy2.rs" },
{ name = "clippy3", path = "../exercises/23_clippy/clippy3.rs" },
{ name = "clippy3_sol", path = "../solutions/23_clippy/clippy3.rs" },
{ name = "using_as", path = "../exercises/24_conversions/using_as.rs" },
{ name = "using_as_sol", path = "../solutions/24_conversions/using_as.rs" },
{ name = "from_into", path = "../exercises/24_conversions/from_into.rs" },
{ name = "from_into_sol", path = "../solutions/24_conversions/from_into.rs" },
{ name = "from_str", path = "../exercises/24_conversions/from_str.rs" },
{ name = "from_str_sol", path = "../solutions/24_conversions/from_str.rs" },
{ name = "try_from_into", path = "../exercises/24_conversions/try_from_into.rs" },
{ name = "try_from_into_sol", path = "../solutions/24_conversions/try_from_into.rs" },
{ name = "as_ref_mut", path = "../exercises/24_conversions/as_ref_mut.rs" },
{ name = "as_ref_mut_sol", path = "../solutions/24_conversions/as_ref_mut.rs" },
]
[package]
name = "exercises"
edition = "2021"
edition = "2024"
# Don't publish the exercises on crates.io!
publish = false
[dependencies]
tokio = { version = "1.45.0", features = ["rt-multi-thread", "macros"] }
[profile.release]
panic = "abort"

View File

@@ -1,7 +1,7 @@
# Variables
In Rust, variables are immutable by default.
When a variable is immutable, once a value is bound to a name, you cant change that value.
When a variable is immutable, once a value is bound to a name, you can't change that value.
You can make them mutable by adding `mut` in front of the variable name.
## Further information

View File

@@ -1,6 +1,6 @@
fn main() {
let number = "T-H-R-E-E"; // Don't change this line
println!("Spell a number: {}", number);
println!("Spell a number: {number}");
// TODO: Fix the compiler error by changing the line below without renaming the variable.
number = 3;

View File

@@ -19,7 +19,7 @@ mod tests {
#[test]
fn yummy_food() {
// This means that calling `picky_eater` with the argument "food" should return "Yummy!".
// This means that calling `picky_eater` with the argument "strawberry" should return "Yummy!".
assert_eq!(picky_eater("strawberry"), "Yummy!");
}

View File

@@ -1,10 +1,10 @@
# Enums
Rust allows you to define types called "enums" which enumerate possible values.
Enums are a feature in many languages, but their capabilities differ in each language. Rusts enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell.
Enums are a feature in many languages, but their capabilities differ in each language. Rust's enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell.
Useful in combination with enums is Rust's "pattern matching" facility, which makes it easy to run different code for different values of an enumeration.
## Further information
- [Enums](https://doc.rust-lang.org/book/ch06-00-enums.html)
- [Pattern syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html)
- [Pattern syntax](https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html)

View File

@@ -23,6 +23,7 @@ mod tests {
assert_eq!(trim_me("Hello! "), "Hello!");
assert_eq!(trim_me(" What's up!"), "What's up!");
assert_eq!(trim_me(" Hola! "), "Hola!");
assert_eq!(trim_me("Hi!"), "Hi!");
}
#[test]

View File

@@ -1,8 +1,8 @@
// This function returns how much icecream there is left in the fridge.
// This function returns how much ice cream there is left in the fridge.
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
// someone eats it all, so no icecream is left (value 0). Return `None` if
// someone eats it all, so no ice cream is left (value 0). Return `None` if
// `hour_of_day` is higher than 23.
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {
// TODO: Complete the function body.
}
@@ -18,19 +18,19 @@ mod tests {
fn raw_value() {
// TODO: Fix this test. How do you get the value contained in the
// Option?
let icecreams = maybe_icecream(12);
let ice_creams = maybe_ice_cream(12);
assert_eq!(icecreams, 5); // Don't change this line.
assert_eq!(ice_creams, 5); // Don't change this line.
}
#[test]
fn check_icecream() {
assert_eq!(maybe_icecream(0), Some(5));
assert_eq!(maybe_icecream(9), Some(5));
assert_eq!(maybe_icecream(18), Some(5));
assert_eq!(maybe_icecream(22), Some(0));
assert_eq!(maybe_icecream(23), Some(0));
assert_eq!(maybe_icecream(24), None);
assert_eq!(maybe_icecream(25), None);
fn check_ice_cream() {
assert_eq!(maybe_ice_cream(0), Some(5));
assert_eq!(maybe_ice_cream(9), Some(5));
assert_eq!(maybe_ice_cream(18), Some(5));
assert_eq!(maybe_ice_cream(22), Some(0));
assert_eq!(maybe_ice_cream(23), Some(0));
assert_eq!(maybe_ice_cream(24), None);
assert_eq!(maybe_ice_cream(25), None);
}
}

View File

@@ -9,7 +9,7 @@ fn main() {
// TODO: Fix the compiler error by adding something to this match statement.
match optional_point {
Some(p) => println!("Co-ordinates are {},{}", p.x, p.y),
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
_ => panic!("No match!"),
}

View File

@@ -1,8 +1,8 @@
# Error handling
Most errors arent serious enough to require the program to stop entirely.
Sometimes, when a function fails, its for a reason that you can easily interpret and respond to.
For example, if you try to open a file and that operation fails because the file doesnt exist, you might want to create the file instead of terminating the process.
Most errors aren't serious enough to require the program to stop entirely.
Sometimes, when a function fails, it's for a reason that you can easily interpret and respond to.
For example, if you try to open a file and that operation fails because the file doesn't exist, you might want to create the file instead of terminating the process.
## Further information

View File

@@ -6,7 +6,7 @@
//
// In short, this particular use case for boxes is for when you want to own a
// value and you care only that it is a type which implements a particular
// trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where
// trait. To do so, the `Box` is declared as of type `Box<dyn Trait>` where
// `Trait` is the trait the compiler looks for on any value used in that
// context. For this exercise, that context is the potential errors which
// can be returned in a `Result`.

View File

@@ -0,0 +1,10 @@
# Async
Rust includes built-in support for asynchronous programming. In other languages, this might be known as Promises or
Coroutines. async programming uses async functions, which are powerful, but may require some getting used to,
especially if you haven't used something similar in another language.
The [relevant book chapter][1] is essential reading. The [tokio docs][2] are also very helpful!
[1]: https://doc.rust-lang.org/book/ch17-00-async-await.html
[2]: https://tokio.rs/tokio/tutorial

View File

@@ -0,0 +1,44 @@
// Our loyal worker works hard to create a new number.
#[derive(Default)]
struct Worker;
struct NumberContainer {
number: i32,
}
impl Worker {
async fn work(&self) -> NumberContainer {
// Pretend this takes a while...
let new_number = 32;
NumberContainer { number: new_number }
}
}
impl NumberContainer {
async fn extract_number(&self) -> i32 {
// And this too...
self.number
}
}
// TODO: Fix the function signature!
fn run_worker() -> i32 {
// TODO: Make our worker create a new number and return it.
}
fn main() {
// Feel free to experiment here. You may need to make some adjustments
// to this function, though.
}
mod tests {
use super::*;
// Don't worry about this attribute for now.
// If you want to know what this does, read the hint!
#[tokio::test]
async fn test_if_it_works() {
let number = run_worker().await;
assert_eq!(number, 32);
}
}

View File

@@ -0,0 +1,42 @@
use tokio::task::JoinSet;
// A MultiWorker can work with the power of 5 normal workers,
// allowing us to create 5 new numbers at once!
struct MultiWorker;
impl MultiWorker {
async fn start_work(&self) -> JoinSet<i32> {
let mut set = JoinSet::new();
for i in 30..35 {
// TODO: `set.spawn` accepts an async function that will return the number
// we want. Implement this function as a closure!
set.spawn(???);
}
set
}
}
async fn run_multi_worker() -> Vec<i32> {
let tasks = MultiWorker.start_work().await;
// TODO: We have a bunch of tasks, how do we run them to completion
// to get at the i32s they create?
}
fn main() {
// Feel free to experiment here. You may need to make some adjustments
// to this function, though.
}
mod tests {
use super::*;
#[tokio::test]
async fn test_if_it_works() {
let mut numbers = run_multi_worker().await;
numbers.sort(); // in case tasks run out-of-order
assert_eq!(numbers, vec![30, 31, 32, 33, 34]);
}
}

View File

@@ -10,5 +10,6 @@ of exercises to Rustlings, but is all about learning to write Macros.
## Further information
- [Macros](https://doc.rust-lang.org/book/ch19-06-macros.html)
- [The Rust Book - Macros](https://doc.rust-lang.org/book/ch20-05-macros.html)
- [The Little Book of Rust Macros](https://veykril.github.io/tlborm/)
- [Rust by Example - macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html)

View File

@@ -4,9 +4,11 @@
#[rustfmt::skip]
#[allow(unused_variables, unused_assignments)]
fn main() {
let my_option: Option<()> = None;
let my_option: Option<&str> = None;
// Assume that you don't know the value of `my_option`.
// In the case of `Some`, we want to print its value.
if my_option.is_none() {
println!("{:?}", my_option.unwrap());
println!("{}", my_option.unwrap());
}
let my_arr = &[

View File

@@ -22,6 +22,7 @@
| iterators | §13.2-4 |
| smart_pointers | §15, §16.3 |
| threads | §16.1-3 |
| async | §17 |
| macros | §19.5 |
| clippy | §21.4 |
| conversions | n/a |

View File

@@ -1,4 +1,7 @@
{
"build": {
"path_prefix": "rustlings"
},
"project": {
"homepage": "https://rustlings.cool",
"repository": "https://github.com/rust-lang/rustlings"

View File

@@ -13,4 +13,4 @@ cargo test --workspace --all-targets
cargo run -- dev check --require-solutions
# MSRV
cargo +1.80 run -- dev check --require-solutions
cargo +1.85 run -- dev check --require-solutions

View File

@@ -16,7 +16,7 @@ include = [
proc-macro = true
[dependencies]
quote = "1.0.37"
quote = "1.0"
serde.workspace = true
toml_edit.workspace = true

View File

@@ -764,7 +764,7 @@ Notice how the trait takes ownership of `self` and returns `Self`.
Although the signature of `append_bar` in the trait takes `self` as argument,
the implementation can take `mut self` instead. This is possible because the
the value is owned anyway."""
value is owned anyway."""
[[exercises]]
name = "traits3"
@@ -1079,11 +1079,51 @@ original sending end.
Related section in The Book:
https://doc.rust-lang.org/book/ch16-02-message-passing.html"""
# ASYNC
[[exercises]]
name = "async1"
dir = "21_async"
test = true
hint = """
Async functions are not the same as normal functions -- they have to be marked with a
special bit of syntax, `async fn`. These functions don't immediately return or even
execute, you have to encourage them to do so by calling another special bit of syntax
on them.
Another thing - an async function can't be properly called in a normal function. Think of
it as something contagious -- everything that it touches needs to be marked as such. Keeping
that in mind, what adjustment do you need to make to the function signature?
Aside:
`#[tokio::test]` (and `#[tokio::main]`) are "magic" attributes that automatically
set up what we call an async runtime. The Rust compiler intentionally doesn't supply
a default implementation of this runtime. Tokio is by far the most popular
community-developed runtime, and this macro does a lot of the heavy lifting to let
us use it.
"""
[[exercises]]
name = "async2"
dir = "21_async"
test = true
hint = """
Async functions can be used to run multiple things in parallel, or to more efficiently
run things on multiple cores. Here, we use Tokio's tasks to schedule some work to run
at the same time. We use a `JoinSet`, which is a list of tasks that lets us decide how
to best execute them.
One of the ways to execute tasks is `JoinSet::join_all`, which even gives us a neat
Vec that we can immediately return! You can also do this sequentially, with an iterator.
See if you can also find a way to do it that doesn't use `JoinSet`! You have access to
most of Tokio's task-based functionality here.
"""
# MACROS
[[exercises]]
name = "macros1"
dir = "21_macros"
dir = "22_macros"
test = false
hint = """
When you call a macro, you need to add something special compared to a regular
@@ -1091,7 +1131,7 @@ function call."""
[[exercises]]
name = "macros2"
dir = "21_macros"
dir = "22_macros"
test = false
hint = """
Macros don't quite play by the same rules as the rest of Rust, in terms of
@@ -1102,7 +1142,7 @@ Unlike other things in Rust, the order of "where you define a macro" versus
[[exercises]]
name = "macros3"
dir = "21_macros"
dir = "22_macros"
test = false
hint = """
In order to use a macro outside of its module, you need to do something
@@ -1110,7 +1150,7 @@ special to the module to lift the macro out into its parent."""
[[exercises]]
name = "macros4"
dir = "21_macros"
dir = "22_macros"
test = false
hint = """
You only need to add a single character to make this compile.
@@ -1127,7 +1167,7 @@ https://veykril.github.io/tlborm/"""
[[exercises]]
name = "clippy1"
dir = "22_clippy"
dir = "23_clippy"
test = false
strict_clippy = true
hint = """
@@ -1144,7 +1184,7 @@ appropriate replacement constant from `std::f32::consts`."""
[[exercises]]
name = "clippy2"
dir = "22_clippy"
dir = "23_clippy"
test = false
strict_clippy = true
hint = """
@@ -1157,7 +1197,7 @@ https://doc.rust-lang.org/std/option/#iterating-over-option"""
[[exercises]]
name = "clippy3"
dir = "22_clippy"
dir = "23_clippy"
test = false
strict_clippy = true
hint = "No hints this time!"
@@ -1166,20 +1206,20 @@ hint = "No hints this time!"
[[exercises]]
name = "using_as"
dir = "23_conversions"
dir = "24_conversions"
hint = """
Use the `as` operator to cast one of the operands in the last line of the
`average` function into the expected return type."""
[[exercises]]
name = "from_into"
dir = "23_conversions"
dir = "24_conversions"
hint = """
Follow the steps provided right before the `From` implementation."""
[[exercises]]
name = "from_str"
dir = "23_conversions"
dir = "24_conversions"
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.
@@ -1196,7 +1236,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen
[[exercises]]
name = "try_from_into"
dir = "23_conversions"
dir = "24_conversions"
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?
@@ -1206,6 +1246,6 @@ types?"""
[[exercises]]
name = "as_ref_mut"
dir = "23_conversions"
dir = "24_conversions"
hint = """
Add `AsRef<str>` or `AsMut<u32>` as a trait bound to the functions."""

View File

@@ -1,6 +1,6 @@
fn main() {
let number = "T-H-R-E-E";
println!("Spell a number: {}", number);
println!("Spell a number: {number}");
// Using variable shadowing
// https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing

View File

@@ -1,9 +1,5 @@
fn bigger(a: i32, b: i32) -> i32 {
if a > b {
a
} else {
b
}
if a > b { a } else { b }
}
fn main() {

View File

@@ -4,8 +4,6 @@ fn main() {
#[cfg(test)]
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_semantics4() {
let mut x = Vec::new();

View File

@@ -26,6 +26,7 @@ mod tests {
assert_eq!(trim_me("Hello! "), "Hello!");
assert_eq!(trim_me(" What's up!"), "What's up!");
assert_eq!(trim_me(" Hola! "), "Hola!");
assert_eq!(trim_me("Hi!"), "Hi!");
}
#[test]

View File

@@ -60,9 +60,11 @@ England,Spain,1,0";
fn build_scores() {
let scores = build_scores_table(RESULTS);
assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"]
.into_iter()
.all(|team_name| scores.contains_key(team_name)));
assert!(
["England", "France", "Germany", "Italy", "Poland", "Spain"]
.into_iter()
.all(|team_name| scores.contains_key(team_name))
);
}
#[test]

View File

@@ -10,7 +10,7 @@ fn main() {
// Solution 1: Matching over the `Option` (not `&Option`) but without moving
// out of the `Some` variant.
match optional_point {
Some(ref p) => println!("Co-ordinates are {},{}", p.x, p.y),
Some(ref p) => println!("Coordinates are {},{}", p.x, p.y),
// ^^^ added
_ => panic!("No match!"),
}
@@ -18,7 +18,8 @@ fn main() {
// Solution 2: Matching over a reference (`&Option`) by added `&` before
// `optional_point`.
match &optional_point {
Some(p) => println!("Co-ordinates are {},{}", p.x, p.y),
//^ added
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
_ => panic!("No match!"),
}

View File

@@ -16,7 +16,7 @@
use std::num::ParseIntError;
#[allow(unused_variables)]
#[allow(unused_variables, clippy::question_mark)]
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;

View File

@@ -29,6 +29,21 @@ impl ParsePosNonzeroError {
}
}
// As an alternative solution, implementing the `From` trait allows for the
// automatic conversion from a `ParseIntError` into a `ParsePosNonzeroError`
// using the `?` operator, without the need to call `map_err`.
//
// ```
// let x: i64 = s.parse()?;
// ```
//
// Traits like `From` will be dealt with in later exercises.
impl From<ParseIntError> for ParsePosNonzeroError {
fn from(err: ParseIntError) -> Self {
ParsePosNonzeroError::ParseInt(err)
}
}
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

View File

@@ -5,11 +5,7 @@
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
// ^^^^ ^^ ^^ ^^
if x.len() > y.len() {
x
} else {
y
}
if x.len() > y.len() { x } else { y }
}
fn main() {

View File

@@ -1,9 +1,5 @@
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
if x.len() > y.len() { x } else { y }
}
fn main() {

View File

@@ -63,12 +63,10 @@ mod tests {
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
saturn.details();
// TODO
let uranus = Planet::Uranus(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
uranus.details();
// TODO
let neptune = Planet::Neptune(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
neptune.details();

View File

@@ -0,0 +1,45 @@
// Our loyal worker works hard to create a new number.
#[derive(Default)]
struct Worker;
struct NumberContainer {
number: i32,
}
impl Worker {
async fn work(&self) -> NumberContainer {
// Pretend this takes a while...
let new_number = 32;
NumberContainer { number: new_number }
}
}
impl NumberContainer {
async fn extract_number(&self) -> i32 {
// And this too...
self.number
}
}
async fn run_worker() -> i32 {
// TODO: Make our worker create a new number and return it.
Worker.work().await.extract_number().await
}
fn main() {
// Feel free to experiment here. You may need to make some adjustments
// to this function, though.
}
mod tests {
use super::*;
// Don't worry about this attribute for now.
// If you want to know what this does, read the hint!
#[tokio::test]
// TODO: Fix the test function signature
fn test_if_it_works() {
let number = run_worker().await;
assert_eq!(number, 32);
}
}

View File

@@ -0,0 +1,43 @@
use tokio::task::JoinSet;
// A MultiWorker can work with the power of 5 normal workers,
// allowing us to create 5 new numbers at once!
struct MultiWorker;
impl MultiWorker {
async fn start_work(&self) -> JoinSet<i32> {
let mut set = JoinSet::new();
for i in 30..35 {
// TODO: `set.spawn` accepts an async function that will return the number
// we want. Implement this function as a closure!
set.spawn(async move { i });
}
set
}
}
async fn run_multi_worker() -> Vec<i32> {
let tasks = MultiWorker.start_work().await;
// TODO: We have a bunch of tasks, how do we run them to completion
// to get at the i32s they create?
tasks.join_all().await
}
fn main() {
// Feel free to experiment here. You may need to make some adjustments
// to this function, though.
}
mod tests {
use super::*;
#[tokio::test]
async fn test_if_it_works() {
let mut numbers = run_multi_worker().await;
numbers.sort(); // in case tasks run out-of-order
assert_eq!(numbers, vec![30, 31, 32, 33, 34]);
}
}

View File

@@ -3,11 +3,11 @@ use std::mem;
#[rustfmt::skip]
#[allow(unused_variables, unused_assignments)]
fn main() {
let my_option: Option<()> = None;
let my_option: Option<&str> = None;
// `unwrap` of an `Option` after checking if it is `None` will panic.
// Use `if-let` instead.
if let Some(value) = my_option {
println!("{value:?}");
println!("{value}");
}
// A comma was missing.
@@ -15,7 +15,7 @@ fn main() {
-1, -2, -3,
-4, -5, -6,
];
println!("My array! Here it is: {:?}", my_arr);
println!("My array! Here it is: {my_arr:?}");
let mut my_empty_vec = vec![1, 2, 3, 4, 5];
// `resize` mutates a vector instead of returning a new one.
@@ -27,5 +27,5 @@ fn main() {
let mut value_b = 66;
// Use `mem::swap` to correctly swap two values.
mem::swap(&mut value_a, &mut value_b);
println!("value a: {}; value b: {}", value_a, value_b);
println!("value a: {value_a}; value b: {value_b}");
}

View File

@@ -62,8 +62,8 @@ mod tests {
// Import `transformer`.
use super::my_module::transformer;
use super::my_module::transformer_iter;
use super::Command;
use super::my_module::transformer_iter;
#[test]
fn it_works() {

View File

@@ -1,11 +1,11 @@
use anyhow::{bail, Context, Error, Result};
use crossterm::{cursor, terminal, QueueableCommand};
use anyhow::{Context, Error, Result, bail};
use crossterm::{QueueableCommand, cursor, terminal};
use std::{
collections::HashSet,
env,
fs::{File, OpenOptions},
io::{Read, Seek, StdoutLock, Write},
path::{Path, MAIN_SEPARATOR_STR},
path::{MAIN_SEPARATOR_STR, Path},
process::{Command, Stdio},
sync::{
atomic::{AtomicUsize, Ordering::Relaxed},
@@ -427,32 +427,34 @@ impl AppState {
let next_exercise_ind = &next_exercise_ind;
let slf = &self;
thread::Builder::new()
.spawn_scoped(s, move || loop {
let exercise_ind = next_exercise_ind.fetch_add(1, Relaxed);
let Some(exercise) = slf.exercises.get(exercise_ind) else {
// No more exercises.
break;
};
.spawn_scoped(s, move || {
loop {
let exercise_ind = next_exercise_ind.fetch_add(1, Relaxed);
let Some(exercise) = slf.exercises.get(exercise_ind) else {
// No more exercises.
break;
};
if exercise_progress_sender
.send((exercise_ind, CheckProgress::Checking))
.is_err()
{
break;
};
if exercise_progress_sender
.send((exercise_ind, CheckProgress::Checking))
.is_err()
{
break;
};
let success = exercise.run_exercise(None, &slf.cmd_runner);
let progress = match success {
Ok(true) => CheckProgress::Done,
Ok(false) => CheckProgress::Pending,
Err(_) => CheckProgress::None,
};
let success = exercise.run_exercise(None, &slf.cmd_runner);
let progress = match success {
Ok(true) => CheckProgress::Done,
Ok(false) => CheckProgress::Pending,
Err(_) => CheckProgress::None,
};
if exercise_progress_sender
.send((exercise_ind, progress))
.is_err()
{
break;
if exercise_progress_sender
.send((exercise_ind, progress))
.is_err()
{
break;
}
}
})
.context("Failed to spawn a thread to check all exercises")?;

View File

@@ -74,13 +74,13 @@ pub fn updated_cargo_toml(
let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?;
let mut updated_cargo_toml = Vec::with_capacity(BINS_BUFFER_CAPACITY);
updated_cargo_toml.extend_from_slice(current_cargo_toml[..bins_start_ind].as_bytes());
updated_cargo_toml.extend_from_slice(&current_cargo_toml.as_bytes()[..bins_start_ind]);
append_bins(
&mut updated_cargo_toml,
exercise_infos,
exercise_path_prefix,
);
updated_cargo_toml.extend_from_slice(current_cargo_toml[bins_end_ind..].as_bytes());
updated_cargo_toml.extend_from_slice(&current_cargo_toml.as_bytes()[bins_end_ind..]);
Ok(updated_cargo_toml)
}

View File

@@ -1,7 +1,7 @@
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use serde::Deserialize;
use std::{
io::Read,
io::{Read, pipe},
path::PathBuf,
process::{Command, Stdio},
};
@@ -17,7 +17,7 @@ fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec<u8>>) ->
};
let mut handle = if let Some(output) = output {
let (mut reader, writer) = os_pipe::pipe().with_context(|| {
let (mut reader, writer) = pipe().with_context(|| {
format!("Failed to create a pipe to run the command `{description}``")
})?;

View File

@@ -1,4 +1,4 @@
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use clap::Subcommand;
use std::path::PathBuf;

View File

@@ -1,8 +1,8 @@
use anyhow::{anyhow, bail, Context, Error, Result};
use anyhow::{Context, Error, Result, anyhow, bail};
use std::{
cmp::Ordering,
collections::HashSet,
fs::{self, read_dir, OpenOptions},
fs::{self, OpenOptions, read_dir},
io::{self, Read, Write},
path::{Path, PathBuf},
process::{Command, Stdio},
@@ -10,11 +10,11 @@ use std::{
};
use crate::{
cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY},
cmd::CmdRunner,
exercise::{RunnableExercise, OUTPUT_CAPACITY},
info_file::{ExerciseInfo, InfoFile},
CURRENT_FORMAT_VERSION,
cargo_toml::{BINS_BUFFER_CAPACITY, append_bins, bins_start_end_ind},
cmd::CmdRunner,
exercise::{OUTPUT_CAPACITY, RunnableExercise},
info_file::{ExerciseInfo, InfoFile},
};
const MAX_N_EXERCISES: usize = 999;
@@ -42,10 +42,14 @@ fn check_cargo_toml(
if old_bins != new_bins {
if cfg!(debug_assertions) {
bail!("The file `dev/Cargo.toml` is outdated. Run `cargo run -- dev update` to update it. Then run `cargo run -- dev check` again");
bail!(
"The file `dev/Cargo.toml` is outdated. Run `cargo dev update` to update it. Then run `cargo run -- dev check` again"
);
}
bail!("The file `Cargo.toml` is outdated. Run `rustlings dev update` to update it. Then run `rustlings dev check` again");
bail!(
"The file `Cargo.toml` is outdated. Run `rustlings dev update` to update it. Then run `rustlings dev check` again"
);
}
Ok(())
@@ -63,7 +67,9 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
bail!("Found an empty exercise name in `info.toml`");
}
if name.len() > MAX_EXERCISE_NAME_LEN {
bail!("The length of the exercise name `{name}` is bigger than the maximum {MAX_EXERCISE_NAME_LEN}");
bail!(
"The length of the exercise name `{name}` is bigger than the maximum {MAX_EXERCISE_NAME_LEN}"
);
}
if let Some(c) = forbidden_char(name) {
bail!("Char `{c}` in the exercise name `{name}` is not allowed");
@@ -79,7 +85,9 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
}
if exercise_info.hint.trim_ascii().is_empty() {
bail!("The exercise `{name}` has an empty hint. Please provide a hint or at least tell the user why a hint isn't needed for this exercise");
bail!(
"The exercise `{name}` has an empty hint. Please provide a hint or at least tell the user why a hint isn't needed for this exercise"
);
}
if !names.insert(name) {
@@ -96,20 +104,28 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
.with_context(|| format!("Failed to read the file {path}"))?;
if !file_buf.contains("fn main()") {
bail!("The `main` function is missing in the file `{path}`.\nCreate at least an empty `main` function to avoid language server errors");
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.");
bail!(
"Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user."
);
}
let contains_tests = file_buf.contains("#[test]\n");
if exercise_info.test {
if !contains_tests {
bail!("The file `{path}` doesn't contain any tests. If you don't want to add tests to this exercise, set `test = false` for this exercise in the `info.toml` file");
bail!(
"The file `{path}` doesn't contain any tests. If you don't want to add tests to this exercise, set `test = false` for this exercise in the `info.toml` file"
);
}
} else if contains_tests {
bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file");
bail!(
"The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file"
);
}
file_buf.clear();
@@ -125,7 +141,10 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
// Only one level of directory nesting is allowed.
fn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet<PathBuf>) -> Result<()> {
let unexpected_file = |path: &Path| {
anyhow!("Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `{dir}` directory", path.display())
anyhow!(
"Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `{dir}` directory",
path.display()
)
};
for entry in read_dir(dir).with_context(|| format!("Failed to open the `{dir}` directory"))? {
@@ -154,7 +173,10 @@ fn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet<PathBuf>) -> R
let path = entry.path();
if !entry.file_type().unwrap().is_file() {
bail!("Found `{}` but expected only files. Only one level of exercise nesting is allowed", path.display());
bail!(
"Found `{}` but expected only files. Only one level of exercise nesting is allowed",
path.display()
);
}
let file_name = path.file_name().unwrap();
@@ -224,8 +246,12 @@ fn check_exercises_unsolved(
fn check_exercises(info_file: &'static InfoFile, cmd_runner: &'static CmdRunner) -> 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"),
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"
),
Ordering::Equal => (),
}
@@ -287,7 +313,7 @@ fn check_solutions(
fmt_cmd
.arg("--check")
.arg("--edition")
.arg("2021")
.arg("2024")
.arg("--color")
.arg("always")
.stdin(Stdio::null());
@@ -353,7 +379,7 @@ pub fn check(require_solutions: bool) -> Result<()> {
}
if cfg!(debug_assertions) {
// A hack to make `cargo run -- dev check` work when developing Rustlings.
// A hack to make `cargo dev check` work when developing Rustlings.
check_cargo_toml(&info_file.exercises, "dev/Cargo.toml", b"../")?;
} else {
check_cargo_toml(&info_file.exercises, "Cargo.toml", b"")?;

View File

@@ -1,4 +1,4 @@
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use std::{
env::set_current_dir,
fs::{self, create_dir},
@@ -6,7 +6,7 @@ use std::{
process::Command,
};
use crate::{init::RUST_ANALYZER_TOML, CURRENT_FORMAT_VERSION};
use crate::{CURRENT_FORMAT_VERSION, init::RUST_ANALYZER_TOML};
// Create a directory relative to the current directory and print its path.
fn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> {
@@ -55,7 +55,9 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> {
write_rel_file(
"info.toml",
&dir_path_str,
format!("{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"),
format!(
"{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"
),
)?;
write_rel_file("Cargo.toml", &dir_path_str, CARGO_TOML)?;
@@ -130,7 +132,7 @@ bin = []
[package]
name = "exercises"
edition = "2021"
edition = "2024"
# Don't publish the exercises on crates.io!
publish = false

View File

@@ -28,7 +28,7 @@ pub fn update() -> Result<()> {
let info_file = InfoFile::parse()?;
if cfg!(debug_assertions) {
// A hack to make `cargo run -- dev update` work when developing Rustlings.
// A hack to make `cargo dev update` work when developing Rustlings.
update_cargo_toml(&info_file.exercises, "dev/Cargo.toml", b"../")
.context("Failed to update the file `dev/Cargo.toml`")?;

View File

@@ -1,13 +1,13 @@
use anyhow::Result;
use crossterm::{
style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},
QueueableCommand,
style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},
};
use std::io::{self, StdoutLock, Write};
use crate::{
cmd::CmdRunner,
term::{self, terminal_file_link, write_ansi, CountedWrite},
term::{self, CountedWrite, terminal_file_link, write_ansi},
};
/// The initial capacity of the output buffer.

View File

@@ -1,4 +1,4 @@
use anyhow::{bail, Context, Error, Result};
use anyhow::{Context, Error, Result, bail};
use serde::Deserialize;
use std::{fs, io::ErrorKind};

View File

@@ -1,7 +1,7 @@
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use crossterm::{
style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},
QueueableCommand,
style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},
};
use serde::Deserialize;
use std::{
@@ -57,7 +57,9 @@ pub fn init() -> Result<()> {
if !workspace_manifest_content.contains("[workspace]\n")
&& !workspace_manifest_content.contains("workspace.")
{
bail!("The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory");
bail!(
"The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory"
);
}
stdout.write_all(b"This command will create the directory `rustlings/` as a member of this Cargo workspace.\nPress ENTER to continue ")?;
@@ -75,7 +77,9 @@ pub fn init() -> Result<()> {
.stdout(Stdio::null())
.status()?;
if !status.success() {
bail!("Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory");
bail!(
"Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory"
);
}
stdout.write_all(b"The directory `rustlings` has been added to `workspace.members` in the `Cargo.toml` file of this Cargo workspace.\n")?;
@@ -174,6 +178,7 @@ const INIT_SOLUTION_FILE: &[u8] = b"fn main() {
pub const RUST_ANALYZER_TOML: &[u8] = br#"check.command = "clippy"
check.extraArgs = ["--profile", "test"]
cargo.targetDir = true
"#;
const GITIGNORE: &[u8] = b"Cargo.lock

View File

@@ -1,14 +1,13 @@
use anyhow::{Context, Result};
use crossterm::{
cursor,
QueueableCommand, cursor,
event::{
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, MouseEventKind,
},
terminal::{
disable_raw_mode, enable_raw_mode, DisableLineWrap, EnableLineWrap, EnterAlternateScreen,
LeaveAlternateScreen,
DisableLineWrap, EnableLineWrap, EnterAlternateScreen, LeaveAlternateScreen,
disable_raw_mode, enable_raw_mode,
},
QueueableCommand,
};
use std::io::{self, StdoutLock, Write};

View File

@@ -1,11 +1,11 @@
use anyhow::{Context, Result};
use crossterm::{
QueueableCommand,
cursor::{MoveTo, MoveToNextLine},
style::{
Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor,
},
terminal::{self, BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate},
QueueableCommand,
};
use std::{
fmt::Write as _,
@@ -15,7 +15,7 @@ use std::{
use crate::{
app_state::AppState,
exercise::Exercise,
term::{progress_bar, CountedWrite, MaxLenWriter},
term::{CountedWrite, MaxLenWriter, progress_bar},
};
use super::scroll_state::ScrollState;

View File

@@ -1,4 +1,4 @@
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use app_state::StateFileStatus;
use clap::{Parser, Subcommand};
use std::{

View File

@@ -1,7 +1,7 @@
use anyhow::Result;
use crossterm::{
style::{Color, ResetColor, SetForegroundColor},
QueueableCommand,
style::{Color, ResetColor, SetForegroundColor},
};
use std::{
io::{self, Write},
@@ -10,7 +10,7 @@ use std::{
use crate::{
app_state::{AppState, ExercisesProgress},
exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY},
exercise::{OUTPUT_CAPACITY, RunnableExercise, solution_link_line},
};
pub fn run(app_state: &mut AppState) -> Result<ExitCode> {

View File

@@ -1,8 +1,8 @@
use crossterm::{
Command, QueueableCommand,
cursor::MoveTo,
style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},
terminal::{Clear, ClearType},
Command, QueueableCommand,
};
use std::{
fmt, fs,

View File

@@ -74,7 +74,9 @@ fn run_watch(
let mut watcher = RecommendedWatcher::new(
notify_event_handler,
Config::default().with_poll_interval(Duration::from_secs(1)),
Config::default()
.with_follow_symlinks(false)
.with_poll_interval(Duration::from_secs(1)),
)
.inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?;

View File

@@ -1,18 +1,18 @@
use anyhow::{Context, Result};
use notify::{
event::{AccessKind, AccessMode, MetadataKind, ModifyKind, RenameMode},
Event, EventKind,
event::{AccessKind, AccessMode, MetadataKind, ModifyKind, RenameMode},
};
use std::{
sync::{
atomic::Ordering::Relaxed,
mpsc::{sync_channel, RecvTimeoutError, Sender, SyncSender},
mpsc::{RecvTimeoutError, Sender, SyncSender, sync_channel},
},
thread,
time::Duration,
};
use super::{WatchEvent, EXERCISE_RUNNING};
use super::{EXERCISE_RUNNING, WatchEvent};
const DEBOUNCE_DURATION: Duration = Duration::from_millis(200);

View File

@@ -1,24 +1,25 @@
use anyhow::{Context, Result};
use crossterm::{
QueueableCommand,
style::{
Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor,
},
terminal, QueueableCommand,
terminal,
};
use std::{
io::{self, Read, StdoutLock, Write},
sync::mpsc::{sync_channel, Sender, SyncSender},
sync::mpsc::{Sender, SyncSender, sync_channel},
thread,
};
use crate::{
app_state::{AppState, ExercisesProgress},
clear_terminal,
exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY},
exercise::{OUTPUT_CAPACITY, RunnableExercise, solution_link_line},
term::progress_bar,
};
use super::{terminal_event::terminal_event_handler, InputPauseGuard, WatchEvent};
use super::{InputPauseGuard, WatchEvent, terminal_event::terminal_event_handler};
const HEADING_ATTRIBUTES: Attributes = Attributes::none()
.with(Attribute::Bold)

View File

@@ -4,7 +4,7 @@ use std::sync::{
mpsc::{Receiver, Sender},
};
use super::{WatchEvent, EXERCISE_RUNNING};
use super::{EXERCISE_RUNNING, WatchEvent};
pub enum InputEvent {
Next,

View File

@@ -7,5 +7,5 @@ bin = [
[package]
name = "test_exercises"
edition = "2021"
edition = "2024"
publish = false