├── .all-contributorsrc
├── .devcontainer
├── devcontainer.json
└── setup.sh
├── .editorconfig
├── .github
├── classroom
│ └── checkresult.js
├── result
│ └── check_result.json
└── workflows
│ └── rust.yml
├── .gitignore
├── .gitpod.yml
├── .markdownlint.yml
├── .vscode
└── extensions.json
├── AUTHORS.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── exercises
├── README.md
├── functions
│ ├── README.md
│ ├── functions1.rs
│ ├── functions2.rs
│ ├── functions3.rs
│ ├── functions4.rs
│ └── functions5.rs
├── if
│ ├── README.md
│ ├── if1.rs
│ ├── if2.rs
│ └── if3.rs
├── intro
│ ├── README.md
│ ├── intro1.rs
│ └── intro2.rs
├── quiz1.rs
└── variables
│ ├── README.md
│ ├── variables1.rs
│ ├── variables2.rs
│ ├── variables3.rs
│ ├── variables4.rs
│ ├── variables5.rs
│ └── variables6.rs
├── flake.lock
├── flake.nix
├── info.toml
├── install.ps1
├── install.sh
├── oranda.json
├── shell.nix
├── src
├── exercise.rs
├── main.rs
├── project.rs
├── run.rs
├── ui.rs
└── verify.rs
└── tests
├── cicv.rs
├── fixture
├── failure
│ ├── compFailure.rs
│ ├── compNoExercise.rs
│ ├── info.toml
│ ├── testFailure.rs
│ └── testNotPassed.rs
├── state
│ ├── finished_exercise.rs
│ ├── info.toml
│ ├── pending_exercise.rs
│ └── pending_test_exercise.rs
└── success
│ ├── compSuccess.rs
│ ├── info.toml
│ └── testSuccess.rs
└── integration_tests.rs
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "image": "mcr.microsoft.com/devcontainers/universal:2-linux",
3 | "waitFor": "onCreateCommand",
4 | "onCreateCommand": ".devcontainer/setup.sh",
5 | "updateContentCommand": "cargo build",
6 | "postCreateCommand": "",
7 | "postAttachCommand": {
8 | "server": "rustlings watch"
9 | },
10 | "customizations": {
11 | "vscode": {
12 | "extensions": [
13 | "rust-lang.rust-analyzer"
14 | ]
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.devcontainer/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | curl https://sh.rustup.rs -sSf | sh -s -- -y
3 |
4 | # Update current shell environment variables after install to find rustup
5 | . "$HOME/.cargo/env"
6 | rustup install stable
7 | cargo install --force --path .
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.rs]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | indent_style = space
7 | indent_size = 4
8 |
--------------------------------------------------------------------------------
/.github/classroom/checkresult.js:
--------------------------------------------------------------------------------
1 | // TODO 判断文件是否存在
2 |
3 | function judge(outputFile) {
4 | try {
5 | let jsonResult = JSON.parse(outputFile);
6 | let points = {};
7 | jsonResult.exercises.forEach(({ name, result }) => {
8 | if (result) {
9 | points[name] = [1,1]
10 | } else {
11 | points[name] = [0,1]
12 | }
13 | })
14 | return points;
15 | } catch(e) {
16 | return {};
17 | }
18 | }
19 |
20 | module.exports.judge = judge;
--------------------------------------------------------------------------------
/.github/result/check_result.json:
--------------------------------------------------------------------------------
1 | {
2 | "exercises": [
3 | {
4 | "name": "intro2",
5 | "result": false
6 | },
7 | {
8 | "name": "move_semantics1",
9 | "result": false
10 | },
11 | {
12 | "name": "functions3",
13 | "result": false
14 | },
15 | {
16 | "name": "primitive_types2",
17 | "result": false
18 | },
19 | {
20 | "name": "options2",
21 | "result": false
22 | },
23 | {
24 | "name": "hashmaps1",
25 | "result": false
26 | },
27 | {
28 | "name": "strings2",
29 | "result": false
30 | },
31 | {
32 | "name": "structs2",
33 | "result": false
34 | },
35 | {
36 | "name": "variables5",
37 | "result": false
38 | },
39 | {
40 | "name": "generics2",
41 | "result": false
42 | },
43 | {
44 | "name": "variables2",
45 | "result": false
46 | },
47 | {
48 | "name": "variables1",
49 | "result": false
50 | },
51 | {
52 | "name": "functions4",
53 | "result": false
54 | },
55 | {
56 | "name": "move_semantics2",
57 | "result": false
58 | },
59 | {
60 | "name": "primitive_types3",
61 | "result": false
62 | },
63 | {
64 | "name": "options3",
65 | "result": false
66 | },
67 | {
68 | "name": "variables6",
69 | "result": false
70 | },
71 | {
72 | "name": "strings3",
73 | "result": false
74 | },
75 | {
76 | "name": "traits1",
77 | "result": false
78 | },
79 | {
80 | "name": "variables3",
81 | "result": false
82 | },
83 | {
84 | "name": "traits4",
85 | "result": false
86 | },
87 | {
88 | "name": "functions5",
89 | "result": false
90 | },
91 | {
92 | "name": "structs3",
93 | "result": false
94 | },
95 | {
96 | "name": "strings4",
97 | "result": false
98 | },
99 | {
100 | "name": "errors1",
101 | "result": false
102 | },
103 | {
104 | "name": "move_semantics3",
105 | "result": false
106 | },
107 | {
108 | "name": "primitive_types4",
109 | "result": false
110 | },
111 | {
112 | "name": "functions1",
113 | "result": false
114 | },
115 | {
116 | "name": "traits2",
117 | "result": false
118 | },
119 | {
120 | "name": "variables4",
121 | "result": false
122 | },
123 | {
124 | "name": "if1",
125 | "result": false
126 | },
127 | {
128 | "name": "traits5",
129 | "result": false
130 | },
131 | {
132 | "name": "modules1",
133 | "result": false
134 | },
135 | {
136 | "name": "errors2",
137 | "result": false
138 | },
139 | {
140 | "name": "enums1",
141 | "result": false
142 | },
143 | {
144 | "name": "move_semantics4",
145 | "result": false
146 | },
147 | {
148 | "name": "primitive_types5",
149 | "result": false
150 | },
151 | {
152 | "name": "lifetimes2",
153 | "result": false
154 | },
155 | {
156 | "name": "traits3",
157 | "result": false
158 | },
159 | {
160 | "name": "functions2",
161 | "result": false
162 | },
163 | {
164 | "name": "if2",
165 | "result": false
166 | },
167 | {
168 | "name": "modules2",
169 | "result": false
170 | },
171 | {
172 | "name": "errors3",
173 | "result": false
174 | },
175 | {
176 | "name": "move_semantics5",
177 | "result": false
178 | },
179 | {
180 | "name": "enums2",
181 | "result": false
182 | },
183 | {
184 | "name": "lifetimes3",
185 | "result": false
186 | },
187 | {
188 | "name": "primitive_types6",
189 | "result": false
190 | },
191 | {
192 | "name": "tests2",
193 | "result": false
194 | },
195 | {
196 | "name": "iterators1",
197 | "result": false
198 | },
199 | {
200 | "name": "modules3",
201 | "result": false
202 | },
203 | {
204 | "name": "if3",
205 | "result": false
206 | },
207 | {
208 | "name": "iterators4",
209 | "result": false
210 | },
211 | {
212 | "name": "tests1",
213 | "result": false
214 | },
215 | {
216 | "name": "enums3",
217 | "result": false
218 | },
219 | {
220 | "name": "move_semantics6",
221 | "result": false
222 | },
223 | {
224 | "name": "vecs1",
225 | "result": false
226 | },
227 | {
228 | "name": "tests3",
229 | "result": false
230 | },
231 | {
232 | "name": "iterators2",
233 | "result": false
234 | },
235 | {
236 | "name": "quiz1",
237 | "result": false
238 | },
239 | {
240 | "name": "tests4",
241 | "result": false
242 | },
243 | {
244 | "name": "vecs2",
245 | "result": false
246 | },
247 | {
248 | "name": "strings1",
249 | "result": false
250 | },
251 | {
252 | "name": "cow1",
253 | "result": false
254 | },
255 | {
256 | "name": "structs1",
257 | "result": false
258 | },
259 | {
260 | "name": "primitive_types1",
261 | "result": false
262 | },
263 | {
264 | "name": "macros1",
265 | "result": false
266 | },
267 | {
268 | "name": "macros3",
269 | "result": false
270 | },
271 | {
272 | "name": "threads2",
273 | "result": false
274 | },
275 | {
276 | "name": "errors4",
277 | "result": false
278 | },
279 | {
280 | "name": "macros4",
281 | "result": false
282 | },
283 | {
284 | "name": "errors5",
285 | "result": false
286 | },
287 | {
288 | "name": "macros2",
289 | "result": false
290 | },
291 | {
292 | "name": "threads3",
293 | "result": false
294 | },
295 | {
296 | "name": "using_as",
297 | "result": false
298 | },
299 | {
300 | "name": "from_into",
301 | "result": false
302 | },
303 | {
304 | "name": "from_str",
305 | "result": false
306 | },
307 | {
308 | "name": "try_from_into",
309 | "result": false
310 | },
311 | {
312 | "name": "as_ref_mut",
313 | "result": false
314 | },
315 | {
316 | "name": "tests5",
317 | "result": false
318 | },
319 | {
320 | "name": "tests6",
321 | "result": false
322 | },
323 | {
324 | "name": "tests7",
325 | "result": false
326 | },
327 | {
328 | "name": "tests8",
329 | "result": false
330 | },
331 | {
332 | "name": "hashmaps2",
333 | "result": false
334 | },
335 | {
336 | "name": "tests9",
337 | "result": false
338 | },
339 | {
340 | "name": "generics1",
341 | "result": false
342 | },
343 | {
344 | "name": "lifetimes1",
345 | "result": false
346 | },
347 | {
348 | "name": "quiz2",
349 | "result": false
350 | },
351 | {
352 | "name": "box1",
353 | "result": false
354 | },
355 | {
356 | "name": "options1",
357 | "result": false
358 | },
359 | {
360 | "name": "arc1",
361 | "result": false
362 | },
363 | {
364 | "name": "clippy1",
365 | "result": false
366 | },
367 | {
368 | "name": "clippy3",
369 | "result": false
370 | },
371 | {
372 | "name": "quiz3",
373 | "result": false
374 | },
375 | {
376 | "name": "clippy2",
377 | "result": false
378 | },
379 | {
380 | "name": "rc1",
381 | "result": false
382 | },
383 | {
384 | "name": "iterators5",
385 | "result": false
386 | },
387 | {
388 | "name": "iterators3",
389 | "result": false
390 | },
391 | {
392 | "name": "threads1",
393 | "result": false
394 | },
395 | {
396 | "name": "errors6",
397 | "result": false
398 | },
399 | {
400 | "name": "hashmaps3",
401 | "result": false
402 | },
403 | {
404 | "name": "algorithm1",
405 | "result": false
406 | },
407 | {
408 | "name": "algorithm2",
409 | "result": false
410 | },
411 | {
412 | "name": "algorithm3",
413 | "result": false
414 | },
415 | {
416 | "name": "algorithm4",
417 | "result": false
418 | },
419 | {
420 | "name": "algorithm5",
421 | "result": false
422 | },
423 | {
424 | "name": "algorithm6",
425 | "result": false
426 | },
427 | {
428 | "name": "algorithm7",
429 | "result": false
430 | },
431 | {
432 | "name": "algorithm8",
433 | "result": false
434 | },
435 | {
436 | "name": "algorithm9",
437 | "result": false
438 | },
439 | {
440 | "name": "algorithm10",
441 | "result": false
442 | }
443 | ],
444 | "user_name": null,
445 | "statistics": {
446 | "total_exercations": 100,
447 | "total_succeeds": 0,
448 | "total_failures": 100,
449 | "total_time": 3
450 | }
451 | }
--------------------------------------------------------------------------------
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: GitHub Classroom Workflow
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | paths-ignore:
7 | - 'README.md'
8 | pull_request:
9 | branches: [ main ]
10 | workflow_dispatch:
11 |
12 | env:
13 | CARGO_TERM_COLOR: always
14 | TZ: Asia/Shanghai # 设置时区
15 | API_URL: "aHR0cHM6Ly9hcGkub3BlbmNhbXAuY24vd2ViL2FwaS9jb3Vyc2VSYW5rL2NyZWF0ZUJ5VGhpcmRUb2tlbg=="
16 | TOKEN: "N1ZkWlBScVpqUklOWElZUk1RZGtBZnZibDFPM1V1"
17 |
18 | jobs:
19 | build:
20 | name: Autograding
21 | runs-on: ubuntu-latest
22 | outputs:
23 | details: ${{ steps.autograding.outputs.details }}
24 | points: ${{ steps.autograding.outputs.points }}
25 | steps:
26 | - name: Decode API URL
27 | id: decode_api_url
28 | run: |
29 | echo "url=$(echo "$API_URL" | base64 --decode)" >> $GITHUB_ENV
30 | - name: Decode Token
31 | id: decode_token
32 | run: |
33 | echo "token=$(echo "$TOKEN" | base64 --decode)" >> $GITHUB_ENV
34 | - uses: actions/checkout@v3
35 | - name: Run tests
36 | run: cargo test --test cicv --verbose
37 | - uses: yfblock/os-autograding@master
38 | id: autograding
39 | with:
40 | outputFile: .github/result/check_result.json
41 | - name: Generate summary JSON
42 | run: |
43 | outfile=".github/result/check_result.json"
44 | summary_file=".github/result/summary.json"
45 |
46 | # 提取需要的值
47 | total_exercations=$(jq '.statistics.total_exercations' $outfile)
48 | total_succeeds=$(jq '.statistics.total_succeeds' $outfile)
49 | github_user="${{ github.actor }}"
50 |
51 | # 生成新的 JSON 内容
52 | new_json=$(jq -n \
53 | --arg channel "github" \
54 | --argjson courseId 1527 \
55 | --arg ext "aaa" \
56 | --arg name "$github_user" \
57 | --argjson score "$total_succeeds" \
58 | --argjson totalScore "$total_exercations" \
59 | '{channel: $channel, courseId: $courseId, ext: $ext, name: $name, score: $score, totalScore: $totalScore}')
60 |
61 | # 保存新的 JSON 文件
62 | echo "$new_json" > $summary_file
63 |
64 | # 打印新的 JSON 文件到终端
65 | cat $summary_file
66 | - name: Post summary JSON to remote API
67 | run: |
68 | summary_file=".github/result/summary.json"
69 |
70 | # 发送 POST 请求
71 | curl -X POST "$url" \
72 | -H "accept: application/json;charset=utf-8" \
73 | -H "Content-Type: application/json" \
74 | -H "token: $token" \
75 | -d "$(cat $summary_file)" \
76 | -v
77 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | target/
3 | **/*.rs.bk
4 | .DS_Store
5 | *.pdb
6 | exercises/clippy/Cargo.toml
7 | exercises/clippy/Cargo.lock
8 | rust-project.json
9 | .idea
10 | .vscode/*
11 | !.vscode/extensions.json
12 | *.iml
13 | *.o
14 | public/
15 |
16 | # Local Netlify folder
17 | .netlify
18 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - init: /workspace/rustlings/install.sh
3 | command: /workspace/.cargo/bin/rustlings watch
4 |
5 | vscode:
6 | extensions:
7 | - rust-lang.rust-analyzer@0.3.1348
8 |
--------------------------------------------------------------------------------
/.markdownlint.yml:
--------------------------------------------------------------------------------
1 | # MD013/line-length Line length, Expected: 80
2 | MD013: false
3 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "rust-lang.rust-analyzer"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | ## 5.5.1 (2023-05-17)
3 |
4 | #### Fixed
5 |
6 | - Reverted `rust-project.json` path generation due to an upstream `rust-analyzer` fix.
7 |
8 |
9 | ## 5.5.0 (2023-05-17)
10 |
11 | #### Added
12 |
13 | - `strings2`: Added a reference to the book chapter for reference conversion
14 | - `lifetimes`: Added a link to the lifetimekata project
15 | - Added a new `tests4` exercises, which teaches about testing for panics
16 | - Added a `!` prefix command to watch mode that runs an external command
17 | - Added a `--success-hints` option to watch mode that shows hints on exercise success
18 |
19 | #### Changed
20 |
21 | - `vecs2`: Renamed iterator variable bindings for clarify
22 | - `lifetimes`: Changed order of book references
23 | - `hashmaps2`: Clarified instructions in the todo block
24 | - Moved lifetime exercises before test exercises (via the recommended book ordering)
25 | - `options2`: Improved tests for layering options
26 | - `modules2`: Added more information to the hint
27 |
28 | #### Fixed
29 |
30 | - `errors2`: Corrected a comment wording
31 | - `iterators2`: Fixed a spelling mistake in the hint text
32 | - `variables`: Wrapped the mut keyword with backticks for readability
33 | - `move_semantics2`: Removed references to line numbers
34 | - `cow1`: Clarified the `owned_no_mutation` comments
35 | - `options3`: Changed exercise to panic when no match is found
36 | - `rustlings lsp` now generates absolute paths, which should fix VSCode `rust-analyzer` usage on Windows
37 |
38 | #### Housekeeping
39 |
40 | - Added a markdown linter to run on GitHub actions
41 | - Split quick installation section into two code blocks
42 |
43 |
44 | ## 5.4.1 (2023-03-10)
45 |
46 | #### Changed
47 |
48 | - `vecs`: Added links to `iter_mut` and `map` to README.md
49 | - `cow1`: Changed main to tests
50 | - `iterators1`: Formatted according to rustfmt
51 |
52 | #### Fixed
53 |
54 | - `errors5`: Unified undisclosed type notation
55 | - `arc1`: Improved readability by avoiding implicit dereference
56 | - `macros4`: Prevented auto-fix by adding `#[rustfmt::skip]`
57 | - `cli`: Actually show correct progress percentages
58 |
59 |
60 |
61 | ## 5.4.0 (2023-02-12)
62 |
63 | #### Changed
64 |
65 | - Reordered exercises
66 | - Unwrapped `standard_library_types` into `iterators` and `smart_pointers`
67 | - Moved smart pointer exercises behind threads
68 | - Ordered `rc1` before `arc1`
69 | - **intro1**: Added a note on `rustlings lsp`
70 | - **threads1**: Panic if threads are not joined
71 | - **cli**:
72 | - Made progress bar update proportional to amount of files verified
73 | - Decreased `watch` delay from 2 to 1 second
74 |
75 | #### Fixed
76 |
77 | - Capitalized "Rust" in exercise hints
78 | - **enums3**: Removed superfluous tuple brackets
79 | - **quiz2, clippy1, iterators1**: Fixed a typo
80 | - **rc1**: Fixed a prompt error
81 | - **cli**:
82 | - Fixed a typo in a method name
83 | - Specified the edition in `rustc` commands
84 |
85 | #### Housekeeping
86 |
87 | - Bumped min Rust version to 1.58 in installation script
88 |
89 |
90 |
91 | ## 5.3.0 (2022-12-23)
92 |
93 | #### Added
94 |
95 | - **cli**: Added a percentage display in watch mode
96 | - Added a `flake.nix` for Nix users
97 |
98 | #### Changed
99 |
100 | - **structs3**: Added an additional test
101 | - **macros**: Added a link to MacroKata in the README
102 |
103 | #### Fixed
104 |
105 | - **strings3**: Added a link to `std` in the hint
106 | - **threads1**: Corrected a hint link
107 | - **iterators1**: Clarified hint steps
108 | - **errors5**: Fix a typo in the hint
109 | - **options1**: Clarified on the usage of the 24-hour system
110 | - **threads2, threads3**: Explicitly use `Arc::clone`
111 | - **structs3**: Clarifed the hint
112 | - **quiz2, as_ref_mut, options1, traits1, traits2**: Clarified hints
113 | - **traits1, traits2, cli**: Tidied up unmatching backticks
114 | - **enums2**: Removed unneccessary indirection of self
115 | - **enums3**: Added an extra tuple comment
116 |
117 | #### Housekeeping
118 |
119 | - Added a VSCode extension recommendation
120 | - Applied some Clippy and rustfmt formatting
121 | - Added a note on Windows PowerShell and other shell compatibility
122 |
123 |
124 |
125 | ## 5.2.1 (2022-09-06)
126 |
127 | #### Fixed
128 |
129 | - **quiz1**: Reworded the comment to actually reflect what's going on in the tests.
130 | Also added another assert just to make sure.
131 | - **rc1**: Fixed a typo in the hint.
132 | - **lifetimes**: Add quotes to the `println!` output, for readability.
133 |
134 | #### Housekeeping
135 |
136 | - Fixed a typo in README.md
137 |
138 |
139 |
140 | ## 5.2.0 (2022-08-27)
141 |
142 | #### Added
143 |
144 | - Added a `reset` command
145 |
146 | #### Changed
147 |
148 | - **options2**: Convert the exercise to use tests
149 |
150 | #### Fixed
151 |
152 | - **threads3**: Fixed a typo
153 | - **quiz1**: Adjusted the explanations to be consistent with
154 | the tests
155 |
156 |
157 |
158 | ## 5.1.1 (2022-08-17)
159 |
160 | #### Bug Fixes
161 |
162 | - Fixed an incorrect assertion in options1
163 |
164 |
165 |
166 | ## 5.1.0 (2022-08-16)
167 |
168 | #### Features
169 |
170 | - Added a new `rc1` exercise.
171 | - Added a new `cow1` exercise.
172 |
173 | #### Bug Fixes
174 |
175 | - **variables5**: Corrected reference to previous exercise
176 | - **functions4**: Fixed line number reference
177 | - **strings3**: Clarified comment wording
178 | - **traits4, traits5**: Fixed line number reference
179 | - **traits5**:
180 | - Fixed typo in "parameter"
181 | - Made exercise prefer a traits-based solution
182 | - **lifetimes2**: Improved hint
183 | - **threads3**: Fixed typo in hint
184 | - **box1**: Replaced `unimplemented!` with `todo!`
185 | - **errors5**: Provided an explanation for usage of `Box`
186 | - **quiz2**: Fixed a typo
187 | - **macros**: Updated the macros book link
188 | - **options1**:
189 | - Removed unused code
190 | - Added more granular tests
191 | - Fixed some comment syntax shenanigans in info.toml
192 |
193 | #### Housekeeping
194 |
195 | - Fixed a typo in .editorconfig
196 | - Fixed a typo in integration_tests.rs
197 | - Clarified manual installation instructions using `cargo install --path .`
198 | - Added a link to our Zulip in the readme file
199 |
200 |
201 |
202 | ## 5.0.0 (2022-07-16)
203 |
204 | #### Features
205 |
206 | - Hint comments in exercises now also include a reference to the
207 | `hint` watch mode subcommand.
208 | - **intro1**: Added more hints to point the user to the source file.
209 | - **variables**: Switched variables3 and variables4.
210 | - Moved `vec` and `primitive_types` exercises before `move_semantics`.
211 | - Renamed `vec` to `vecs` to be more in line with the naming in general.
212 | - Split up the `collections` exercises in their own folders.
213 | - **vec2**: Added a second part of the function that provides an alternative,
214 | immutable way of modifying vec values.
215 | - **enums3**: Added a hint.
216 | - Moved `strings` before `modules`.
217 | - Added a `strings3` exercise to teach modifying strings.
218 | - Added a `hashmaps3` exercise for some advanced usage of hashmaps.
219 | - Moved the original `quiz2` to be `strings4`, since it only tested strings
220 | anyways.
221 | - Reworked `quiz2` into a new exercise that tests more chapters.
222 | - Renamed `option` to `options`.
223 | - **options1**: Rewrote parts of the exercise to remove the weird array
224 | iteration stuff.
225 | - Moved `generics3` to be `quiz3`.
226 | - Moved box/arc exercises behind `iterators`.
227 | - **iterators4**: Added a test for factorials of zero.
228 | - Split `threads1` between two exercises, the first one focusing more on
229 | `JoinHandle`s.
230 | - Added a `threads3` exercises that uses `std::sync::mpsc`.
231 | - Added a `clippy3` exercises with some more interesting checks.
232 | - **as_ref_mut**: Added a section that actually tests `AsMut`.
233 | - Added 3 new lifetimes exercises.
234 | - Added 3 new traits exercises.
235 |
236 | #### Bug Fixes
237 |
238 | - **variables2**: Made output messages more verbose.
239 | - **variables5**: Added a nudging hint about shadowing.
240 | - **variables6**: Fixed link to book.
241 | - **functions**: Clarified the README wording. Generally cleaned up
242 | some hints and added some extra comments.
243 | - **if2**: Renamed function name to `foo_if_fizz`.
244 | - **move_semantics**: Clarified some hints.
245 | - **quiz1**: Renamed the function name to be more verbose.
246 | - **structs1**: Use an integer type instead of strings. Renamed "unit structs"
247 | to "unit-like structs", as is used in the book.
248 | - **structs3**: Added the `panic!` statement in from the beginning.
249 | - **errors1**: Use `is_empty()` instead of `len() > 0`
250 | - **errors3**: Improved the hint.
251 | - **errors5**: Improved exercise instructions and the hint.
252 | - **errors6**: Provided the skeleton of one of the functions that's supposed
253 | to be implemented.
254 | - **iterators3**: Inserted `todo!` into `divide()` to keep a compiler error
255 | from happening.
256 | - **from_str**: Added a hint comment about string error message conversion with
257 | `Box`.
258 | - **try_from_into**: Fixed the function name in comment.
259 |
260 | #### Removed
261 |
262 | - Removed the legacy LSP feature that was using `mod.rs` files.
263 | - Removed `quiz4`.
264 | - Removed `advanced_errs`. These were the last exercises in the recommended
265 | order, and I've always felt like they didn't quite fit in with the mostly
266 | simple, book-following style we've had in Rustlings.
267 |
268 | #### Housekeeping
269 |
270 | - Added missing exercises to the book index.
271 | - Updated spacing in Cargo.toml.
272 | - Added a GitHub actions config so that tests run on every PR/commit.
273 |
274 |
275 |
276 | ## 4.8.0 (2022-07-01)
277 |
278 | #### Features
279 |
280 | - Added a progress indicator for `rustlings watch`.
281 | - The installation script now checks for Rustup being installed.
282 | - Added a `rustlings lsp` command to enable `rust-analyzer`.
283 |
284 | #### Bug Fixes
285 |
286 | - **move_semantics5**: Replaced "in vogue" with "in scope" in hint.
287 | - **if2**: Fixed a typo in the hint.
288 | - **variables1**: Fixed an incorrect line reference in the hint.
289 | - Fixed an out of bounds check in the installation Bash script.
290 |
291 | #### Housekeeping
292 |
293 | - Replaced the git.io URL with the fully qualified URL because of git.io's sunsetting.
294 | - Removed the deprecated Rust GitPod extension.
295 |
296 |
297 |
298 | ## 4.7.1 (2022-04-20)
299 |
300 | #### Features
301 |
302 | - The amount of dependency crates that need to be compiled went down from ~65 to
303 | ~45 by bumping dependency versions.
304 | - The minimum Rust version in the install scripts has been bumped to 1.56.0 (this isn't in
305 | the release itself, since install scripts don't really get versioned)
306 |
307 | #### Bug Fixes
308 |
309 | - **arc1**: A small part has been rewritten using a more functional code style (#968).
310 | - **using_as**: A small part has been refactored to use `sum` instead of `fold`, resulting
311 | in better readability.
312 |
313 | #### Housekeeping
314 |
315 | - The changelog will now be manually written instead of being automatically generated by the
316 | Git log.
317 |
318 |
319 |
320 | ## 4.7.0 (2022-04-14)
321 |
322 | #### Features
323 |
324 | - Add move_semantics6.rs exercise (#908) ([3f0e1303](https://github.com/rust-lang/rustlings/commit/3f0e1303e0b3bf3fecc0baced3c8b8a37f83c184))
325 | - **intro:** Add intro section. ([21c9f441](https://github.com/rust-lang/rustlings/commit/21c9f44168394e08338fd470b5f49b1fd235986f))
326 | - Include exercises folder in the project structure behind a feature, enabling rust-analyzer to work (#917) ([179a75a6](https://github.com/rust-lang/rustlings/commit/179a75a68d03ac9518dec2297fb17f91a4fc506b))
327 |
328 | #### Bug Fixes
329 |
330 | - Fix a few spelling mistakes ([1c0fe3cb](https://github.com/rust-lang/rustlings/commit/1c0fe3cbcca85f90b3985985b8e265ee872a2ab2))
331 | - **cli:**
332 | - Move long text strings into constants. ([f78c4802](https://github.com/rust-lang/rustlings/commit/f78c48020830d7900dd8d81f355606581670446d))
333 | - Replace `filter_map()` with `find_map()` ([9b27e8d](https://github.com/rust-lang/rustlings/commit/9b27e8d993ca20232fe38a412750c3f845a83b65))
334 | - **clippy1:**
335 | - Set clippy::float_cmp lint to deny (#907) ([71a06044](https://github.com/rust-lang/rustlings/commit/71a06044e6a96ff756dc31d7b0ed665ae4badb57))
336 | - Updated code to test correctness clippy lint with approx_constant lint rule ([f2650de3](https://github.com/rust-lang/rustlings/commit/f2650de369810867d2763e935ac0963c32ec420e))
337 | - **errors1:**
338 | - Add a comment to make the purpose more clear (#486) ([cbcde345](https://github.com/rust-lang/rustlings/commit/cbcde345409c3e550112e449242848eaa3391bb6))
339 | - Don't modify tests (#958) ([60bb7cc](https://github.com/rust-lang/rustlings/commit/60bb7cc3931d21d3986ad52b2b302e632a93831c))
340 | - **errors6:** Remove existing answer code ([43d0623](https://github.com/rust-lang/rustlings/commit/43d0623086edbc46fe896ba59c7afa22c3da9f7a))
341 | - **functions5:** Remove wrong new line and small English improvements (#885) ([8ef4869b](https://github.com/rust-lang/rustlings/commit/8ef4869b264094e5a9b50452b4534823a9df19c3))
342 | - **install:** protect path with whitespaces using quotes and stop at the first error ([d114847f](https://github.com/rust-lang/rustlings/commit/d114847f256c5f571c0b4c87e04b04bce3435509))
343 | - **intro1:** Add compiler error explanation. ([9b8de655](https://github.com/rust-lang/rustlings/commit/9b8de65525a5576b78cf0c8e4098cdd34296338f))
344 | - **iterators1:** reorder TODO steps ([0bd7a063](https://github.com/rust-lang/rustlings/commit/0bd7a0631a17a9d69af5746795a30efc9cf64e6e))
345 | - **move_semantics2:** Add comment ([89650f80](https://github.com/rust-lang/rustlings/commit/89650f808af23a32c9a2c6d46592b77547a6a464))
346 | - **move_semantics5:** correct typo (#857) ([46c28d5c](https://github.com/rust-lang/rustlings/commit/46c28d5cef3d8446b5a356b19d8dbc725f91a3a0))
347 | - **quiz1:** update to say quiz covers "If" ([1622e8c1](https://github.com/rust-lang/rustlings/commit/1622e8c198d89739765c915203efff0091bdeb78))
348 | - **structs3:**
349 | - Add a hint for panic (#608) ([4f7ff5d9](https://github.com/rust-lang/rustlings/commit/4f7ff5d9c7b2d8b045194c1a9469d37e30257c4a))
350 | - remove redundant 'return' (#852) ([bf33829d](https://github.com/rust-lang/rustlings/commit/bf33829da240375d086f96267fc2e02fa6b07001))
351 | - Assigned value to `cents_per_gram` in test ([d1ee2da](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532))
352 | - **structs3.rs:** assigned value to cents_per_gram in test ([d1ee2daf](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532))
353 | - **traits1:** rename test functions to snake case (#854) ([1663a16e](https://github.com/rust-lang/rustlings/commit/1663a16eade6ca646b6ed061735f7982434d530d))
354 |
355 | #### Documentation improvements
356 |
357 | - Add hints on how to get GCC installed (#741) ([bc56861](https://github.com/rust-lang/rustlings/commit/bc5686174463ad6f4f6b824b0e9b97c3039d4886))
358 | - Fix some code blocks that were not highlighted ([17f9d74](https://github.com/rust-lang/rustlings/commit/17f9d7429ccd133a72e815fb5618e0ce79560929))
359 |
360 |
361 |
362 | ## 4.6.0 (2021-09-25)
363 |
364 | #### Features
365 |
366 | - add advanced_errs2 ([abd6b70c](https://github.com/rust-lang/rustlings/commit/abd6b70c72dc6426752ff41f09160b839e5c449e))
367 | - add advanced_errs1 ([882d535b](https://github.com/rust-lang/rustlings/commit/882d535ba8628d5e0b37e8664b3e2f26260b2671))
368 | - Add a farewell message when quitting `watch` ([1caef0b4](https://github.com/rust-lang/rustlings/commit/1caef0b43494c8b8cdd6c9260147e70d510f1aca))
369 | - add more watch commands ([a7dc080b](https://github.com/rust-lang/rustlings/commit/a7dc080b95e49146fbaafe6922a6de2f8cb1582a), closes [#842](https://github.com/rust-lang/rustlings/issues/842))
370 | - **modules:** update exercises, add modules3 (#822) ([dfd2fab4](https://github.com/rust-lang/rustlings/commit/dfd2fab4f33d1bf59e2e5ee03123c0c9a67a9481))
371 | - **quiz1:** add default function name in comment (#838) ([0a11bad7](https://github.com/rust-lang/rustlings/commit/0a11bad71402b5403143d642f439f57931278c07))
372 |
373 | #### Bug Fixes
374 |
375 | - Correct small typo in exercises/conversions/from_str.rs ([86cc8529](https://github.com/rust-lang/rustlings/commit/86cc85295ae36948963ae52882e285d7e3e29323))
376 | - **cli:** typo in exercise.rs (#848) ([06d5c097](https://github.com/rust-lang/rustlings/commit/06d5c0973a3dffa3c6c6f70acb775d4c6630323c))
377 | - **from_str, try_from_into:** custom error types ([2dc93cad](https://github.com/rust-lang/rustlings/commit/2dc93caddad43821743e4903d89b355df58d7a49))
378 | - **modules2:** fix typo (#835) ([1c3beb0a](https://github.com/rust-lang/rustlings/commit/1c3beb0a59178c950dc05fe8ee2346b017429ae0))
379 | - **move_semantics5:**
380 | - change &mut \*y to &mut x (#814) ([d75759e8](https://github.com/rust-lang/rustlings/commit/d75759e829fdcd64ef071cf4b6eae2a011a7718b))
381 | - Clarify instructions ([df25684c](https://github.com/rust-lang/rustlings/commit/df25684cb79f8413915e00b5efef29369849cef1))
382 | - **quiz1:** Fix inconsistent wording (#826) ([03131a3d](https://github.com/rust-lang/rustlings/commit/03131a3d35d9842598150f9da817f7cc26e2669a))
383 |
384 |
385 |
386 | ## 4.5.0 (2021-07-07)
387 |
388 | #### Features
389 |
390 | - Add move_semantics5 exercise. (#746) ([399ab328](https://github.com/rust-lang/rustlings/commit/399ab328d8d407265c09563aa4ef4534b2503ff2))
391 | - **cli:** Add "next" to run the next unsolved exercise. (#785) ([d20e413a](https://github.com/rust-lang/rustlings/commit/d20e413a68772cd493561f2651cf244e822b7ca5))
392 |
393 | #### Bug Fixes
394 |
395 | - rename result1 to errors4 ([50ab289d](https://github.com/rust-lang/rustlings/commit/50ab289da6b9eb19a7486c341b00048c516b88c0))
396 | - move_semantics5 hints ([1b858285](https://github.com/rust-lang/rustlings/commit/1b85828548f46f58b622b5e0c00f8c989f928807))
397 | - remove trailing whitespaces from iterators1 ([4d4fa774](https://github.com/rust-lang/rustlings/commit/4d4fa77459392acd3581c6068aa8be9a02de12fc))
398 | - add hints to generics1 and generics2 exercises ([31457940](https://github.com/rust-lang/rustlings/commit/31457940846b3844d78d4a4d2b074bc8d6aaf1eb))
399 | - remove trailing whitespace ([d9b69bd1](https://github.com/rust-lang/rustlings/commit/d9b69bd1a0a7a99f2c0d80933ad2eea44c8c71b2))
400 | - **installation:** first PowerShell command ([aa9a943d](https://github.com/rust-lang/rustlings/commit/aa9a943ddf3ae260782e73c26bcc9db60e5894b6))
401 | - **iterators5:** derive Clone, Copy ([91fc9e31](https://github.com/rust-lang/rustlings/commit/91fc9e3118f4af603c9911698cc2a234725cb032))
402 | - **quiz1:** Updated question description (#794) ([d8766496](https://github.com/rust-lang/rustlings/commit/d876649616cc8a8dd5f539f8bc1a5434b960b1e9))
403 | - **try_from_into, from_str:** hints for dyn Error ([11d2cf0d](https://github.com/rust-lang/rustlings/commit/11d2cf0d604dee3f5023c17802d69438e69fa50e))
404 | - **variables5:** confine the answer further ([48ffcbd2](https://github.com/rust-lang/rustlings/commit/48ffcbd2c4cc4d936c2c7480019190f179813cc5))
405 |
406 |
407 |
408 | ## 4.4.0 (2021-04-24)
409 |
410 | #### Bug Fixes
411 |
412 | - Fix spelling error in main.rs ([91ee27f2](https://github.com/rust-lang/rustlings/commit/91ee27f22bd3797a9db57e5fd430801c170c5db8))
413 | - typo in default out text ([644c49f1](https://github.com/rust-lang/rustlings/commit/644c49f1e04cbb24e95872b3a52b07d692ae3bc8))
414 | - **collections:** Naming exercises for vectors and hashmap ([bef39b12](https://github.com/rust-lang/rustlings/commit/bef39b125961310b34b34871e480a82e82af4678))
415 | - **from_str:**
416 | - Correct typos ([5f7c89f8](https://github.com/rust-lang/rustlings/commit/5f7c89f85db1f33da01911eaa479c3a2d4721678))
417 | - test for error instead of unwrap/should_panic ([15e71535](https://github.com/rust-lang/rustlings/commit/15e71535f37cfaed36e22eb778728d186e2104ab))
418 | - use trait objects for from_str ([c3e7b831](https://github.com/rust-lang/rustlings/commit/c3e7b831786c9172ed8bd5d150f3c432f242fba9))
419 | - **functions3:** improve function argument type (#687) ([a6509cc4](https://github.com/rust-lang/rustlings/commit/a6509cc4d545d8825f01ddf7ee37823b372154dd))
420 | - **hashmap2:** Update incorrect assertion (#660) ([72aaa15e](https://github.com/rust-lang/rustlings/commit/72aaa15e6ab4b72b3422f1c6356396e20a2a2bb8))
421 | - **info:** Fix typo (#635) ([cddc1e86](https://github.com/rust-lang/rustlings/commit/cddc1e86e7ec744ee644cc774a4887b1a0ded3e8))
422 | - **iterators2:** Moved errors out of tests. ([baf4ba17](https://github.com/rust-lang/rustlings/commit/baf4ba175ba6eb92989e3dd54ecbec4bedc9a863), closes [#359](https://github.com/rust-lang/rustlings/issues/359))
423 | - **iterators3:** Enabled iterators3.rs to run without commented out tests. ([c6712dfc](https://github.com/rust-lang/rustlings/commit/c6712dfccd1a093e590ad22bbc4f49edc417dac0))
424 | - **main:** Let find_exercise work with borrows ([347f30bd](https://github.com/rust-lang/rustlings/commit/347f30bd867343c5ace1097e085a1f7e356553f7))
425 | - **move_semantics4:**
426 | - Remove redundant "instead" (#640) ([cc266d7d](https://github.com/rust-lang/rustlings/commit/cc266d7d80b91e79df3f61984f231b7f1587218e))
427 | - Small readbility improvement (#617) ([10965920](https://github.com/rust-lang/rustlings/commit/10965920fbdf8a1efc85bed869e55a1787006404))
428 | - **option2:** Rename uninformative variables (#675) ([b4de6594](https://github.com/rust-lang/rustlings/commit/b4de6594380636817d13c2677ec6f472a964cf43))
429 | - **quiz3:** Force an answer to Q2 (#672) ([0d894e6f](https://github.com/rust-lang/rustlings/commit/0d894e6ff739943901e1ae8c904582e5c2f843bd))
430 | - **structs:** Add 5.3 to structs/README (#652) ([6bd791f2](https://github.com/rust-lang/rustlings/commit/6bd791f2f44aa7f0ad926df767f6b1fa8f12a9a9))
431 | - **structs2:** correct grammar in hint (#663) ([ebdb66c7](https://github.com/rust-lang/rustlings/commit/ebdb66c7bfb6d687a14cc511a559a222e6fc5de4))
432 | - **structs3:**
433 | - reword heading comment (#664) ([9f3e8c2d](https://github.com/rust-lang/rustlings/commit/9f3e8c2dde645e5264c2d2200e68842b5f47bfa3))
434 | - add check to prevent naive implementation of is_international ([05a753fe](https://github.com/rust-lang/rustlings/commit/05a753fe6333d36dbee5f68c21dec04eacdc75df))
435 | - **threads1:** line number correction ([7857b0a6](https://github.com/rust-lang/rustlings/commit/7857b0a689b0847f48d8c14cbd1865e3b812d5ca))
436 | - **try_from_into:** use trait objects ([2e93a588](https://github.com/rust-lang/rustlings/commit/2e93a588e0abe8badb7eafafb9e7d073c2be5df8))
437 |
438 | #### Features
439 |
440 | - Replace clap with argh ([7928122f](https://github.com/rust-lang/rustlings/commit/7928122fcef9ca7834d988b1ec8ca0687478beeb))
441 | - Replace emojis when NO_EMOJI env variable present ([8d62a996](https://github.com/rust-lang/rustlings/commit/8d62a9963708dbecd9312e8bcc4b47049c72d155))
442 | - Added iterators5.rs exercise. ([b29ea17e](https://github.com/rust-lang/rustlings/commit/b29ea17ea94d1862114af2cf5ced0e09c197dc35))
443 | - **arc1:** Add more details to description and hint (#710) ([81be4044](https://github.com/rust-lang/rustlings/commit/81be40448777fa338ebced3b0bfc1b32d6370313))
444 | - **cli:** Improve the list command with options, and then some ([8bbe4ff1](https://github.com/rust-lang/rustlings/commit/8bbe4ff1385c5c169c90cd3ff9253f9a91daaf8e))
445 | - **list:**
446 | - updated progress percentage ([1c6f7e4b](https://github.com/rust-lang/rustlings/commit/1c6f7e4b7b9b3bd36f4da2bb2b69c549cc8bd913))
447 | - added progress info ([c0e3daac](https://github.com/rust-lang/rustlings/commit/c0e3daacaf6850811df5bc57fa43e0f249d5cfa4))
448 |
449 |
450 |
451 | ## 4.3.0 (2020-12-29)
452 |
453 | #### Features
454 |
455 | - Rewrite default out text ([44d39112](https://github.com/rust-lang/rustlings/commit/44d39112ff122b29c9793fe52e605df1612c6490))
456 | - match exercise order to book chapters (#541) ([033bf119](https://github.com/rust-lang/rustlings/commit/033bf1198fc8bfce1b570e49da7cde010aa552e3))
457 | - Crab? (#586) ([fa9f522b](https://github.com/rust-lang/rustlings/commit/fa9f522b7f043d7ef73a39f003a9272dfe72c4f4))
458 | - add "rustlings list" command ([838f9f30](https://github.com/rust-lang/rustlings/commit/838f9f30083d0b23fd67503dcf0fbeca498e6647))
459 | - **try_from_into:** remove duplicate annotation ([04f1d079](https://github.com/rust-lang/rustlings/commit/04f1d079aa42a2f49af694bc92c67d731d31a53f))
460 |
461 | #### Bug Fixes
462 |
463 | - update structs README ([bcf14cf6](https://github.com/rust-lang/rustlings/commit/bcf14cf677adb3a38a3ac3ca53f3c69f61153025))
464 | - added missing exercises to info.toml ([90cfb6ff](https://github.com/rust-lang/rustlings/commit/90cfb6ff28377531bfc34acb70547bdb13374f6b))
465 | - gives a bit more context to magic number ([30644c9a](https://github.com/rust-lang/rustlings/commit/30644c9a062b825c0ea89435dc59f0cad86b110e))
466 | - **functions2:** Change signature to trigger precise error message: (#605) ([0ef95947](https://github.com/rust-lang/rustlings/commit/0ef95947cc30482e63a7045be6cc2fb6f6dcb4cc))
467 | - **structs1:** Adjust wording (#573) ([9334783d](https://github.com/rust-lang/rustlings/commit/9334783da31d821cc59174fbe8320df95828926c))
468 | - **try_from_into:**
469 | - type error ([4f4cfcf3](https://github.com/rust-lang/rustlings/commit/4f4cfcf3c36c8718c7c170c9c3a6935e6ef0618c))
470 | - Update description (#584) ([96347df9](https://github.com/rust-lang/rustlings/commit/96347df9df294f01153b29d9ad4ba361f665c755))
471 | - **vec1:** Have test compare every element in a and v ([9b6c6293](https://github.com/rust-lang/rustlings/commit/9b6c629397b24b944f484f5b2bbd8144266b5695))
472 |
473 |
474 |
475 | ## 4.2.0 (2020-11-07)
476 |
477 | #### Features
478 |
479 | - Add HashMap exercises ([633c00cf](https://github.com/rust-lang/rustlings/commit/633c00cf8071e1e82959a3010452a32f34f29fc9))
480 | - Add Vec exercises ([0c12fa31](https://github.com/rust-lang/rustlings/commit/0c12fa31c57c03c6287458a0a8aca7afd057baf6))
481 | - **primitive_types6:** Add a test (#548) ([2b1fb2b7](https://github.com/rust-lang/rustlings/commit/2b1fb2b739bf9ad8d6b7b12af25fee173011bfc4))
482 | - **try_from_into:** Add tests (#571) ([95ccd926](https://github.com/rust-lang/rustlings/commit/95ccd92616ae79ba287cce221101e0bbe4f68cdc))
483 |
484 | #### Bug Fixes
485 |
486 | - log error output when inotify limit is exceeded ([d61b4e5a](https://github.com/rust-lang/rustlings/commit/d61b4e5a13b44d72d004082f523fa1b6b24c1aca))
487 | - more unique temp_file ([5643ef05](https://github.com/rust-lang/rustlings/commit/5643ef05bc81e4a840e9456f4406a769abbe1392))
488 | - **installation:** Update the MinRustVersion ([21bfb2d4](https://github.com/rust-lang/rustlings/commit/21bfb2d4777429c87d8d3b5fbf0ce66006dcd034))
489 | - **iterators2:** Update description (#578) ([197d3a3d](https://github.com/rust-lang/rustlings/commit/197d3a3d8961b2465579218a6749b2b2cefa8ddd))
490 | - **primitive_types6:**
491 | - remove 'unused doc comment' warning ([472d8592](https://github.com/rust-lang/rustlings/commit/472d8592d65c8275332a20dfc269e7ac0d41bc88))
492 | - missing comma in test ([4fb230da](https://github.com/rust-lang/rustlings/commit/4fb230daf1251444fcf29e085cee222a91f8a37e))
493 | - **quiz3:** Second test is for odd numbers, not even. (#553) ([18e0bfef](https://github.com/rust-lang/rustlings/commit/18e0bfef1de53071e353ba1ec5837002ff7290e6))
494 |
495 |
496 |
497 | ## 4.1.0 (2020-10-05)
498 |
499 | #### Bug Fixes
500 |
501 | - Update rustlings version in Cargo.lock ([1cc40bc9](https://github.com/rust-lang/rustlings/commit/1cc40bc9ce95c23d56f6d91fa1c4deb646231fef))
502 | - **arc1:** index mod should equal thread count ([b4062ef6](https://github.com/rust-lang/rustlings/commit/b4062ef6993e80dac107c4093ea85166ad3ee0fa))
503 | - **enums3:** Update Message::ChangeColor to take a tuple. (#457) ([4b6540c7](https://github.com/rust-lang/rustlings/commit/4b6540c71adabad647de8a09e57295e7c7c7d794))
504 | - **exercises:** adding question mark to quiz2 ([101072ab](https://github.com/rust-lang/rustlings/commit/101072ab9f8c80b40b8b88cb06cbe38aca2481c5))
505 | - **generics3:** clarify grade change ([47f7672c](https://github.com/rust-lang/rustlings/commit/47f7672c0307732056e7426e81d351f0dd7e22e5))
506 | - **structs3:** Small adjustment of variable name ([114b54cb](https://github.com/rust-lang/rustlings/commit/114b54cbdb977234b39e5f180d937c14c78bb8b2))
507 | - **using_as:** Add test so that proper type is returned. (#512) ([3286c5ec](https://github.com/rust-lang/rustlings/commit/3286c5ec19ea5fb7ded81d047da5f8594108a490))
508 |
509 | #### Features
510 |
511 | - Added iterators1.rs exercise ([9642f5a3](https://github.com/rust-lang/rustlings/commit/9642f5a3f686270a4f8f6ba969919ddbbc4f7fdd))
512 | - Add ability to run rustlings on repl.it (#471) ([8f7b5bd0](https://github.com/rust-lang/rustlings/commit/8f7b5bd00eb83542b959830ef55192d2d76db90a))
513 | - Add gitpod support (#473) ([4821a8be](https://github.com/rust-lang/rustlings/commit/4821a8be94af4f669042a06ab917934cfacd032f))
514 | - Remind the user of the hint option (#425) ([816b1f5e](https://github.com/rust-lang/rustlings/commit/816b1f5e85d6cc6e72673813a85d0ada2a8f84af))
515 | - Remind the user of the hint option (#425) ([9f61db5d](https://github.com/rust-lang/rustlings/commit/9f61db5dbe38538cf06571fcdd5f806e7901e83a))
516 | - **cli:** Added 'cls' command to 'watch' mode (#474) ([4f2468e1](https://github.com/rust-lang/rustlings/commit/4f2468e14f574a93a2e9b688367b5752ed96ae7b))
517 | - **try_from_into:** Add insufficient length test (#469) ([523d18b8](https://github.com/rust-lang/rustlings/commit/523d18b873a319f7c09262f44bd40e2fab1830e5))
518 |
519 |
520 |
521 | ## 4.0.0 (2020-07-08)
522 |
523 | #### Breaking Changes
524 |
525 | - Add a --nocapture option to display test harnesses' outputs ([8ad5f9bf](https://github.com/rust-lang/rustlings/commit/8ad5f9bf531a4848b1104b7b389a20171624c82f))
526 | - Rename test to quiz, fixes #244 ([010a0456](https://github.com/rust-lang/rustlings/commit/010a04569282149cea7f7a76fc4d7f4c9f0f08dd))
527 |
528 | #### Features
529 |
530 | - Add traits README ([173bb141](https://github.com/rust-lang/rustlings/commit/173bb14140c5530cbdb59e53ace3991a99d804af))
531 | - Add box1.rs exercise ([7479a473](https://github.com/rust-lang/rustlings/commit/7479a4737bdcac347322ad0883ca528c8675e720))
532 | - Rewrite try_from_into (#393) ([763aa6e3](https://github.com/rust-lang/rustlings/commit/763aa6e378a586caae2d8d63755a85eeba227933))
533 | - Add if2 exercise ([1da84b5f](https://github.com/rust-lang/rustlings/commit/1da84b5f7c489f65bd683c244f13c7d1ee812df0))
534 | - Added exercise structs3.rs ([b66e2e09](https://github.com/rust-lang/rustlings/commit/b66e2e09622243e086a0f1258dd27e1a2d61c891))
535 | - Add exercise variables6 covering const (#352) ([5999acd2](https://github.com/rust-lang/rustlings/commit/5999acd24a4f203292be36e0fd18d385887ec481))
536 |
537 | #### Bug Fixes
538 |
539 | - Change then to than ([ddd98ad7](https://github.com/rust-lang/rustlings/commit/ddd98ad75d3668fbb10eff74374148aa5ed2344d))
540 | - rename quiz1 to tests1 in info (#420) ([0dd1c6ca](https://github.com/rust-lang/rustlings/commit/0dd1c6ca6b389789e0972aa955fe17aa15c95f29))
541 | - fix quiz naming inconsistency (#421) ([5563adbb](https://github.com/rust-lang/rustlings/commit/5563adbb890587fc48fbbc9c4028642687f1e85b))
542 | - confine the user further in variable exercises ([06ef4cc6](https://github.com/rust-lang/rustlings/commit/06ef4cc654e75d22a526812919ee49b8956280bf))
543 | - update iterator and macro text for typos and clarity ([95900828](https://github.com/rust-lang/rustlings/commit/959008284834bece0196a01e17ac69a7e3590116))
544 | - update generics2 closes #362 ([964c974a](https://github.com/rust-lang/rustlings/commit/964c974a0274199d755073b917c2bc5da0c9b4f1))
545 | - confusing comment in conversions/try_from_into.rs ([c9e4f2cf](https://github.com/rust-lang/rustlings/commit/c9e4f2cfb4c48d0b7451263cfb43b9426438122d))
546 | - **arc1:** Passively introduce attributes (#429) ([113cdae2](https://github.com/rust-lang/rustlings/commit/113cdae2d4e4c55905e8056ad326ede7fd7de356))
547 | - **box1:** fix comment typo (#426) ([bb2ca251](https://github.com/rust-lang/rustlings/commit/bb2ca251106b27a7272d9a30872904dd1376654c))
548 | - **errorsn:** Try harder to confine the user. (#388) ([2b20c8a0](https://github.com/rust-lang/rustlings/commit/2b20c8a0f5774d07c58d110d75879f33fc6273b5))
549 | - **from_into.rs:** typo ([a901499e](https://github.com/rust-lang/rustlings/commit/a901499ededd3ce1995164700514fe4e9a0373ea))
550 | - **generics2:** Guide students to the answer (#430) ([e6bd8021](https://github.com/rust-lang/rustlings/commit/e6bd8021d9a7dd06feebc30c9d5f953901d7b419))
551 | - **installation:**
552 | - Provide a backup git reference when tag can't be curl ([9e4fb100](https://github.com/rust-lang/rustlings/commit/9e4fb1009f1c9e3433915c03e22c2af422e5c5fe))
553 | - Check if python is available while checking for git,rustc and cargo ([9cfb617d](https://github.com/rust-lang/rustlings/commit/9cfb617d5b0451b4b51644a1298965390cda9884))
554 | - **option1:**
555 | - Don't add only zeros to the numbers array ([cce6a442](https://github.com/rust-lang/rustlings/commit/cce6a4427718724a9096800754cd3abeca6a1580))
556 | - Add cast to usize, as it is confusing in the context of an exercise about Option ([f6cffc7e](https://github.com/rust-lang/rustlings/commit/f6cffc7e487b42f15a6f958e49704c93a8d4465b))
557 | - **option2:** Add TODO to comments (#400) ([10967bce](https://github.com/rust-lang/rustlings/commit/10967bce57682812dc0891a9f9757da1a9d87404))
558 | - **options1:** Add hint about Array Initialization (#389) ([9f75554f](https://github.com/rust-lang/rustlings/commit/9f75554f2a30295996f03f0160b98c0458305502))
559 | - **test2:** name of type String and &str (#394) ([d6c0a688](https://github.com/rust-lang/rustlings/commit/d6c0a688e6a96f93ad60d540d4b326f342fc0d45))
560 | - **variables6:** minor typo (#419) ([524e17df](https://github.com/rust-lang/rustlings/commit/524e17df10db95f7b90a0f75cc8997182a8a4094))
561 |
562 |
563 |
564 | ## 3.0.0 (2020-04-11)
565 |
566 | #### Breaking Changes
567 |
568 | - make "compile" exercises print output (#278) ([3b6d5c](https://github.com/fmoko/rustlings/commit/3b6d5c3aaa27a242a832799eb66e96897d26fde3))
569 |
570 | #### Bug Fixes
571 |
572 | - **primitive_types:** revert primitive_types4 (#296) ([b3a3351e](https://github.com/rust-lang/rustlings/commit/b3a3351e8e6a0bdee07077d7b0382953821649ae))
573 | - **run:** compile clippy exercise files (#295) ([3ab084a4](https://github.com/rust-lang/rustlings/commit/3ab084a421c0f140ae83bf1fc3f47b39342e7373))
574 | - **conversions:**
575 | - add additional test to meet exercise rules (#284) ([bc22ec3](https://github.com/fmoko/rustlings/commit/bc22ec382f843347333ef1301fc1bad773657f38))
576 | - remove duplicate not done comment (#292) ([dab90f](https://github.com/fmoko/rustlings/commit/dab90f7b91a6000fe874e3d664f244048e5fa342))
577 | - don't hardcode documentation version for traits (#288) ([30e6af](https://github.com/fmoko/rustlings/commit/30e6af60690c326fb5d3a9b7335f35c69c09137d))
578 |
579 | #### Features
580 |
581 | - add Option2 exercise (#290) ([86b5c08b](https://github.com/rust-lang/rustlings/commit/86b5c08b9bea1576127a7c5f599f5752072c087d))
582 | - add exercise for option (#282) ([135e5d47](https://github.com/rust-lang/rustlings/commit/135e5d47a7c395aece6f6022117fb20c82f2d3d4))
583 | - add new exercises for generics (#280) ([76be5e4e](https://github.com/rust-lang/rustlings/commit/76be5e4e991160f5fd9093f03ee2ba260e8f7229))
584 | - **ci:** add buildkite config ([b049fa2c](https://github.com/rust-lang/rustlings/commit/b049fa2c84dba0f0c8906ac44e28fd45fba51a71))
585 |
586 |
587 |
588 | ### 2.2.1 (2020-02-27)
589 |
590 | #### Bug Fixes
591 |
592 | - Re-add cloning the repo to install scripts ([3d9b03c5](https://github.com/rust-lang/rustlings/commit/3d9b03c52b8dc51b140757f6fd25ad87b5782ef5))
593 |
594 | #### Features
595 |
596 | - Add clippy lints (#269) ([1e2fd9c9](https://github.com/rust-lang/rustlings/commit/1e2fd9c92f8cd6e389525ca1a999fca4c90b5921))
597 |
598 |
599 |
600 | ## 2.2.0 (2020-02-25)
601 |
602 | #### Bug Fixes
603 |
604 | - Update deps to version compatable with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401))
605 | - **docs:**
606 | - Added a necessary step to Windows installation process (#242) ([3906efcd](https://github.com/rust-lang/rustlings/commit/3906efcd52a004047b460ed548037093de3f523f))
607 | - Fixed mangled sentence from book; edited for clarity (#266) ([ade52ff](https://github.com/rust-lang/rustlings/commit/ade52ffb739987287ddd5705944c8777705faed9))
608 | - Updated iterators readme to account for iterators4 exercise (#273) ([bec8e3a](https://github.com/rust-lang/rustlings/commit/bec8e3a644cbd88db1c73ea5f1d8a364f4a34016))
609 | - **installation:** make fatal errors more obvious (#272) ([17d0951e](https://github.com/rust-lang/rustlings/commit/17d0951e66fda8e11b204d5c4c41a0d5e22e78f7))
610 | - **iterators2:**
611 | - Remove reference to missing iterators2.rs (#245) ([419f7797](https://github.com/rust-lang/rustlings/commit/419f7797f294e4ce6a2b883199731b5bde77d262))
612 | - **as_ref_mut:** Enable a test and improve per clippy's suggestion (#256) ([dfdf809](https://github.com/rust-lang/rustlings/commit/dfdf8093ebbd4145864995627b812780de52f902))
613 | - **tests1:**
614 | - Change test command ([fe10e06c](https://github.com/rust-lang/rustlings/commit/fe10e06c3733ddb4a21e90d09bf79bfe618e97ce)
615 | - Correct test command in tests1.rs comment (#263) ([39fa7ae](https://github.com/rust-lang/rustlings/commit/39fa7ae8b70ad468da49b06f11b2383135a63bcf))
616 |
617 | #### Features
618 |
619 | - Add variables5.rs exercise (#264) ([0c73609e](https://github.com/rust-lang/rustlings/commit/0c73609e6f2311295e95d6f96f8c747cfc4cba03))
620 | - Show a completion message when watching (#253) ([d25ee55a](https://github.com/rust-lang/rustlings/commit/d25ee55a3205882d35782e370af855051b39c58c))
621 | - Add type conversion and parsing exercises (#249) ([0c85dc11](https://github.com/rust-lang/rustlings/commit/0c85dc1193978b5165491b99cc4922caf8d14a65))
622 | - Created consistent money unit (#258) ([fd57f8f](https://github.com/rust-lang/rustlings/commit/fd57f8f2c1da2af8ddbebbccec214e6f40f4dbab))
623 | - Enable test for exercise test4 (#276) ([8b971ff](https://github.com/rust-lang/rustlings/commit/8b971ffab6079a706ac925f5917f987932b55c07))
624 | - Added traits exercises (#274 but specifically #216, which originally added
625 | this :heart:) ([b559cdd](https://github.com/rust-lang/rustlings/commit/b559cdd73f32c0d0cfc1feda39f82b3e3583df17))
626 |
627 |
628 |
629 | ## 2.1.0 (2019-11-27)
630 |
631 | #### Bug Fixes
632 |
633 | - add line numbers in several exercises and hints ([b565c4d3](https://github.com/rust-lang/rustlings/commit/b565c4d3e74e8e110bef201a082fa1302722a7c3))
634 | - **arc1:** Fix some words in the comment ([c42c3b21](https://github.com/rust-lang/rustlings/commit/c42c3b2101df9164c8cd7bb344def921e5ba3e61))
635 | - **enums:** Add link to chapter on pattern syntax (#242) ([615ce327](https://github.com/rust-lang/rustlings/commit/615ce3279800c56d89f19d218ccb7ef576624feb))
636 | - **primitive_types4:**
637 | - update outdated hint ([4c5189df](https://github.com/rust-lang/rustlings/commit/4c5189df2bdd9a231f6b2611919ba5aa14da0d3f))
638 | - update outdated comment ([ded2c034](https://github.com/rust-lang/rustlings/commit/ded2c034ba93fa1e3c2c2ea16b83abc1a57265e8))
639 | - **strings2:** update line number in hint ([a09f684f](https://github.com/rust-lang/rustlings/commit/a09f684f05c58d239a6fc59ec5f81c2533e8b820))
640 | - **variables1:** Correct wrong word in comment ([fda5a470](https://github.com/rust-lang/rustlings/commit/fda5a47069e0954f16a04e8e50945e03becb71a5))
641 |
642 | #### Features
643 |
644 | - **watch:** show hint while watching ([8143d57b](https://github.com/rust-lang/rustlings/commit/8143d57b4e88c51341dd4a18a14c536042cc009c))
645 |
646 |
647 |
648 | ## 2.0.0 (2019-11-12)
649 |
650 | #### Bug Fixes
651 |
652 | - **default:** Clarify the installation procedure ([c371b853](https://github.com/rust-lang/rustlings/commit/c371b853afa08947ddeebec0edd074b171eeaae0))
653 | - **info:** Fix trailing newlines for hints ([795b6e34](https://github.com/rust-lang/rustlings/commit/795b6e348094a898e9227a14f6232f7bb94c8d31))
654 | - **run:** make `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3))
655 |
656 | #### Breaking Changes
657 |
658 | - Refactor hint system ([9bdb0a12](https://github.com/rust-lang/rustlings/commit/9bdb0a12e45a8e9f9f6a4bd4a9c172c5376c7f60))
659 | - improve `watch` execution mode ([2cdd6129](https://github.com/rust-lang/rustlings/commit/2cdd61294f0d9a53775ee24ad76435bec8a21e60))
660 | - Index exercises by name ([627cdc07](https://github.com/rust-lang/rustlings/commit/627cdc07d07dfe6a740e885e0ddf6900e7ec336b))
661 | - **run:** makes `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3))
662 |
663 | #### Features
664 |
665 | - **cli:** check for rustc before doing anything ([36a033b8](https://github.com/rust-lang/rustlings/commit/36a033b87a6549c1e5639c908bf7381c84f4f425))
666 | - **hint:** Add test for hint ([ce9fa6eb](https://github.com/rust-lang/rustlings/commit/ce9fa6ebbfdc3e7585d488d9409797285708316f))
667 |
668 |
669 |
670 | ### 1.5.1 (2019-11-11)
671 |
672 | #### Bug Fixes
673 |
674 | - **errors3:** Update hint ([dcfb427b](https://github.com/rust-lang/rustlings/commit/dcfb427b09585f0193f0a294443fdf99f11c64cb), closes [#185](https://github.com/rust-lang/rustlings/issues/185))
675 | - **if1:** Remove `return` reference ([ad03d180](https://github.com/rust-lang/rustlings/commit/ad03d180c9311c0093e56a3531eec1a9a70cdb45))
676 | - **strings:** Move Strings before Structs ([6dcecb38](https://github.com/rust-lang/rustlings/commit/6dcecb38a4435593beb87c8e12d6314143631482), closes [#204](https://github.com/rust-lang/rustlings/issues/204))
677 | - **structs1:** Remove misleading comment ([f72e5a8f](https://github.com/rust-lang/rustlings/commit/f72e5a8f05568dde04eaeac10b9a69872f21cb37))
678 | - **threads:** Move Threads behind SLT ([fbe91a67](https://github.com/rust-lang/rustlings/commit/fbe91a67a482bfe64cbcdd58d06ba830a0f39da3), closes [#205](https://github.com/rust-lang/rustlings/issues/205))
679 | - **watch:** clear screen before each `verify()` ([3aff590](https://github.com/rust-lang/rustlings/commit/3aff59085586c24196a547c2693adbdcf4432648))
680 |
681 |
682 |
683 | ## 1.5.0 (2019-11-09)
684 |
685 | #### Bug Fixes
686 |
687 | - **test1:** Rewrite logic ([79a56942](https://github.com/rust-lang/rustlings/commit/79a569422c8309cfc9e4aed25bf4ab3b3859996b))
688 | - **installation:** Fix rustlings installation check ([7a252c47](https://github.com/rust-lang/rustlings/commit/7a252c475551486efb52f949b8af55803b700bc6))
689 | - **iterators:** Rename iterator3.rs ([433d2115](https://github.com/rust-lang/rustlings/commit/433d2115bc1c04b6d34a335a18c9a8f3e2672bc6))
690 | - **iterators2:** Remove syntax resulting in misleading error message ([4cde8664](https://github.com/rust-lang/rustlings/commit/4cde86643e12db162a66e62f23b78962986046ac))
691 | - **option1:**
692 | - Fix arguments passed to assert! macro (#222) ([4c2cf6da](https://github.com/rust-lang/rustlings/commit/4c2cf6da755efe02725e05ecc3a303304c10a6da))
693 | - Fix arguments passed to assert! macro ([ead4f7af](https://github.com/rust-lang/rustlings/commit/ead4f7af9e10e53418efdde5c359159347282afd))
694 | - Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6))
695 | - **primitive_types4:** Fail on a slice covering the wrong area ([5b1e673c](https://github.com/rust-lang/rustlings/commit/5b1e673cec1658afc4ebbbc800213847804facf5))
696 | - **readme:** http to https ([70946b85](https://github.com/rust-lang/rustlings/commit/70946b85e536e80e70ed9505cb650ca0a3a1fbb5))
697 | - **test1:**
698 | - Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446))
699 | - renamed function name to snake case closes #180 ([89d5186c](https://github.com/rust-lang/rustlings/commit/89d5186c0dae8135ecabf90ee8bb35949bc2d29b))
700 |
701 | #### Features
702 |
703 | - Add enums exercises ([dc150321](https://github.com/rust-lang/rustlings/commit/dc15032112fc485226a573a18139e5ce928b1755))
704 | - Added exercise for struct update syntax ([1c4c8764](https://github.com/rust-lang/rustlings/commit/1c4c8764ed118740cd4cee73272ddc6cceb9d959))
705 | - **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031))
706 |
707 |
708 |
709 | ### 1.4.1 (2019-08-13)
710 |
711 | #### Bug Fixes
712 |
713 | - **iterators2:** Remove syntax resulting in misleading error message ([4cde8664](https://github.com/rust-lang/rustlings/commit/4cde86643e12db162a66e62f23b78962986046ac))
714 | - **option1:** Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6))
715 | - **test1:** Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446))
716 |
717 |
718 |
719 | ## 1.4.0 (2019-07-13)
720 |
721 | #### Bug Fixes
722 |
723 | - **installation:** Fix rustlings installation check ([7a252c47](https://github.com/rust-lang/rustlings/commit/7a252c475551486efb52f949b8af55803b700bc6))
724 | - **iterators:** Rename iterator3.rs ([433d2115](https://github.com/rust-lang/rustlings/commit/433d2115bc1c04b6d34a335a18c9a8f3e2672bc6))
725 | - **readme:** http to https ([70946b85](https://github.com/rust-lang/rustlings/commit/70946b85e536e80e70ed9505cb650ca0a3a1fbb5))
726 | - **test1:** renamed function name to snake case ([89d5186c](https://github.com/rust-lang/rustlings/commit/89d5186c0dae8135ecabf90ee8bb35949bc2d29b))
727 | - **cli:** Check if changed exercise file exists before calling verify ([ba85ca3](https://github.com/rust-lang/rustlings/commit/ba85ca32c4cfc61de46851ab89f9c58a28f33c88))
728 | - **structs1:** Fix the irrefutable let pattern warning ([cc6a141](https://github.com/rust-lang/rustlings/commit/cc6a14104d7c034eadc98297eaaa972d09c50b1f))
729 |
730 | #### Features
731 |
732 | - **changelog:** Use clog for changelogs ([34e31232](https://github.com/rust-lang/rustlings/commit/34e31232dfddde284a341c9609b33cd27d9d5724))
733 | - **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031))
734 |
735 |
736 |
737 | ### 1.3.0 (2019-06-05)
738 |
739 | #### Features
740 |
741 | - Adds a simple exercise for structures (#163, @briankung)
742 |
743 | #### Bug Fixes
744 |
745 | - Add Result type signature as it is difficult for new comers to understand Generics and Error all at once. (#157, @veggiemonk)
746 | - Rustfmt and whitespace fixes (#161, @eddyp)
747 | - errorsn.rs: Separate also the hints from each other to avoid accidental viewing (#162, @eddyp)
748 | - fixed outdated links (#165, @gushroom)
749 | - Fix broken link (#164, @HanKruiger)
750 | - Remove highlighting and syntect (#167, @komaeda)
751 |
752 |
753 |
754 | ### 1.2.2 (2019-05-07)
755 |
756 | #### Bug Fixes
757 |
758 | - Reverted `--nocapture` flag since it was causing tests to pass unconditionally
759 |
760 |
761 |
762 | ### 1.2.1 (2019-04-22)
763 |
764 | #### Bug Fixes
765 |
766 | - Fix the `--nocapture` feature (@komaeda)
767 | - Provide a nicer error message for when you're in the wrong directory
768 |
769 |
770 |
771 | ### 1.2.0 (2019-04-22)
772 |
773 | #### Features
774 |
775 | - Add errors to exercises that compile without user changes (@yvan-sraka)
776 | - Use --nocapture when testing, enabling `println!` when running (@komaeda)
777 |
778 |
779 |
780 | ### 1.1.1 (2019-04-14)
781 |
782 | #### Bug fixes
783 |
784 | - Fix permissions on exercise files (@zacanger, #133)
785 | - Make installation checks more thorough (@komaeda, 1b3469f236bc6979c27f6e1a04e4138a88e55de3)
786 | - Fix order of true/false in tests for executables (@mgeier, #137)
787 | - Stop run from panicking when compile fails (@cjpearce, #141)
788 | - Fix intermittent test failure caused by race condition (@cjpearce, #140)
789 | - Fix links by deleting book version (@diodfr, #142)
790 | - Canonicalize paths to fix path matching (@cjpearce, #143)
791 |
792 |
793 |
794 | ### 1.1.0 (2019-03-20)
795 |
796 | - errors2.rs: update link to Rust book (#124)
797 | - Start verification at most recently modified file (#120)
798 | - Watch for file creation events in watch mode (#117)
799 | - Add standard library types to exercises suite (#119)
800 | - Give a warning when Rustlings isn't run from the right directory (#123)
801 | - Verify that rust version is recent enough to install Rustlings (#131)
802 |
803 |
804 |
805 | ### 1.0.1 (2019-03-06)
806 |
807 | - Adds a way to install Rustlings in one command (`curl -L https://git.io/rustlings | bash`)
808 | - Makes `rustlings watch` react to create file events (@shaunbennett, #117)
809 | - Reworks the exercise management to use an external TOML file instead of just listing them in the code
810 |
811 |
812 |
813 | ### 1.0.0 (2019-03-06)
814 |
815 | Initial release.
816 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing to Rustlings
2 |
3 | First off, thanks for taking the time to contribute!! ❤️
4 |
5 | ### Quick Reference
6 |
7 | I want to...
8 |
9 | _add an exercise! ➡️ [read this](#addex) and then [open a Pull Request](#prs)_
10 |
11 | _update an outdated exercise! ➡️ [open a Pull Request](#prs)_
12 |
13 | _report a bug! ➡️ [open an Issue](#issues)_
14 |
15 | _fix a bug! ➡️ [open a Pull Request](#prs)_
16 |
17 | _implement a new feature! ➡️ [open an Issue to discuss it first, then a Pull Request](#issues)_
18 |
19 |
20 | ### Working on the source code
21 |
22 | `rustlings` is basically a glorified `rustc` wrapper. Therefore the source code
23 | isn't really that complicated since the bulk of the work is done by `rustc`.
24 | `src/main.rs` contains a simple `argh` CLI that connects to most of the other source files.
25 |
26 |
27 | ### Adding an exercise
28 |
29 | The first step is to add the exercise! Name the file `exercises/yourTopic/yourTopicN.rs`, make sure to
30 | put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`.
31 |
32 | Next make sure it runs with `rustlings`. The exercise metadata is stored in `info.toml`, under the `exercises` array. The order of the `exercises` array determines the order the exercises are run by `rustlings verify` and `rustlings watch`.
33 |
34 | Add the metadata for your exercise in the correct order in the `exercises` array. If you are unsure of the correct ordering, add it at the bottom and ask in your pull request. The exercise metadata should contain the following:
35 | ```diff
36 | ...
37 | + [[exercises]]
38 | + name = "yourTopicN"
39 | + path = "exercises/yourTopic/yourTopicN.rs"
40 | + mode = "compile"
41 | + hint = """
42 | + Some kind of useful hint for your exercise."""
43 | ...
44 | ```
45 |
46 | The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have tests to verify in your exercise, choose `test`, otherwise `compile`. If you're working on a Clippy exercise, use `mode = "clippy"`.
47 |
48 | That's all! Feel free to put up a pull request.
49 |
50 |
51 | ### Issues
52 |
53 | You can open an issue [here](https://github.com/rust-lang/rustlings/issues/new).
54 | If you're reporting a bug, please include the output of the following commands:
55 |
56 | - `rustc --version`
57 | - `rustlings --version`
58 | - `ls -la`
59 | - Your OS name and version
60 |
61 |
62 | ### Pull Requests
63 |
64 | Opening a pull request is as easy as forking the repository and committing your
65 | changes. There's a couple of things to watch out for:
66 |
67 | #### Write correct commit messages
68 |
69 | We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/)
70 | specification.
71 | This means that you have to format your commit messages in a specific way. Say
72 | you're working on adding a new exercise called `foobar1.rs`. You could write
73 | the following commit message:
74 |
75 | ```
76 | feat: add foobar1.rs exercise
77 | ```
78 |
79 | If you're just fixing a bug, please use the `fix` type:
80 |
81 | ```
82 | fix(verify): make sure verify doesn't self-destruct
83 | ```
84 |
85 | The scope within the brackets is optional, but should be any of these:
86 |
87 | - `installation` (for the installation script)
88 | - `cli` (for general CLI changes)
89 | - `verify` (for the verification source file)
90 | - `watch` (for the watch functionality source)
91 | - `run` (for the run functionality source)
92 | - `EXERCISENAME` (if you're changing a specific exercise, or set of exercises,
93 | substitute them here)
94 |
95 | When the commit also happens to close an existing issue, link it in the message
96 | body:
97 |
98 | ```
99 | fix: update foobar
100 |
101 | closes #101029908
102 | ```
103 |
104 | If you're doing simple changes, like updating a book link, use `chore`:
105 |
106 | ```
107 | chore: update exercise1.rs book link
108 | ```
109 |
110 | If you're updating documentation, use `docs`:
111 |
112 | ```
113 | docs: add more information to Readme
114 | ```
115 |
116 | If, and only if, you're absolutely sure you want to make a breaking change
117 | (please discuss this beforehand!), add an exclamation mark to the type and
118 | explain the breaking change in the message body:
119 |
120 | ```
121 | fix!: completely change verification
122 |
123 | BREAKING CHANGE: This has to be done because lorem ipsum dolor
124 | ```
125 |
126 | #### Pull Request Workflow
127 |
128 | Once you open a Pull Request, it may be reviewed or labeled (or both) until
129 | the maintainers accept your change. Please be patient, it may take some time
130 | for this to happen!
131 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "addr2line"
7 | version = "0.20.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
10 | dependencies = [
11 | "gimli",
12 | ]
13 |
14 | [[package]]
15 | name = "adler"
16 | version = "1.0.2"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
19 |
20 | [[package]]
21 | name = "aho-corasick"
22 | version = "1.0.2"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
25 | dependencies = [
26 | "memchr",
27 | ]
28 |
29 | [[package]]
30 | name = "argh"
31 | version = "0.1.10"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e"
34 | dependencies = [
35 | "argh_derive",
36 | "argh_shared",
37 | ]
38 |
39 | [[package]]
40 | name = "argh_derive"
41 | version = "0.1.10"
42 | source = "registry+https://github.com/rust-lang/crates.io-index"
43 | checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6"
44 | dependencies = [
45 | "argh_shared",
46 | "proc-macro2",
47 | "quote",
48 | "syn 1.0.109",
49 | ]
50 |
51 | [[package]]
52 | name = "argh_shared"
53 | version = "0.1.10"
54 | source = "registry+https://github.com/rust-lang/crates.io-index"
55 | checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f"
56 |
57 | [[package]]
58 | name = "assert_cmd"
59 | version = "0.11.1"
60 | source = "registry+https://github.com/rust-lang/crates.io-index"
61 | checksum = "2dc477793bd82ec39799b6f6b3df64938532fdf2ab0d49ef817eac65856a5a1e"
62 | dependencies = [
63 | "escargot",
64 | "predicates",
65 | "predicates-core",
66 | "predicates-tree",
67 | ]
68 |
69 | [[package]]
70 | name = "autocfg"
71 | version = "1.1.0"
72 | source = "registry+https://github.com/rust-lang/crates.io-index"
73 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
74 |
75 | [[package]]
76 | name = "backtrace"
77 | version = "0.3.68"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 | checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
80 | dependencies = [
81 | "addr2line",
82 | "cc",
83 | "cfg-if 1.0.0",
84 | "libc",
85 | "miniz_oxide",
86 | "object",
87 | "rustc-demangle",
88 | ]
89 |
90 | [[package]]
91 | name = "bitflags"
92 | version = "1.3.2"
93 | source = "registry+https://github.com/rust-lang/crates.io-index"
94 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
95 |
96 | [[package]]
97 | name = "bytes"
98 | version = "1.4.0"
99 | source = "registry+https://github.com/rust-lang/crates.io-index"
100 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
101 |
102 | [[package]]
103 | name = "cc"
104 | version = "1.0.79"
105 | source = "registry+https://github.com/rust-lang/crates.io-index"
106 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
107 |
108 | [[package]]
109 | name = "cfg-if"
110 | version = "0.1.10"
111 | source = "registry+https://github.com/rust-lang/crates.io-index"
112 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
113 |
114 | [[package]]
115 | name = "cfg-if"
116 | version = "1.0.0"
117 | source = "registry+https://github.com/rust-lang/crates.io-index"
118 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
119 |
120 | [[package]]
121 | name = "console"
122 | version = "0.15.7"
123 | source = "registry+https://github.com/rust-lang/crates.io-index"
124 | checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
125 | dependencies = [
126 | "encode_unicode",
127 | "lazy_static",
128 | "libc",
129 | "unicode-width",
130 | "windows-sys 0.45.0",
131 | ]
132 |
133 | [[package]]
134 | name = "difference"
135 | version = "2.0.0"
136 | source = "registry+https://github.com/rust-lang/crates.io-index"
137 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
138 |
139 | [[package]]
140 | name = "encode_unicode"
141 | version = "0.3.6"
142 | source = "registry+https://github.com/rust-lang/crates.io-index"
143 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
144 |
145 | [[package]]
146 | name = "escargot"
147 | version = "0.4.0"
148 | source = "registry+https://github.com/rust-lang/crates.io-index"
149 | checksum = "ceb9adbf9874d5d028b5e4c5739d22b71988252b25c9c98fe7cf9738bee84597"
150 | dependencies = [
151 | "lazy_static",
152 | "log",
153 | "serde",
154 | "serde_json",
155 | ]
156 |
157 | [[package]]
158 | name = "filetime"
159 | version = "0.2.21"
160 | source = "registry+https://github.com/rust-lang/crates.io-index"
161 | checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
162 | dependencies = [
163 | "cfg-if 1.0.0",
164 | "libc",
165 | "redox_syscall 0.2.16",
166 | "windows-sys 0.48.0",
167 | ]
168 |
169 | [[package]]
170 | name = "float-cmp"
171 | version = "0.8.0"
172 | source = "registry+https://github.com/rust-lang/crates.io-index"
173 | checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
174 | dependencies = [
175 | "num-traits",
176 | ]
177 |
178 | [[package]]
179 | name = "fsevent"
180 | version = "0.4.0"
181 | source = "registry+https://github.com/rust-lang/crates.io-index"
182 | checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
183 | dependencies = [
184 | "bitflags",
185 | "fsevent-sys",
186 | ]
187 |
188 | [[package]]
189 | name = "fsevent-sys"
190 | version = "2.0.1"
191 | source = "registry+https://github.com/rust-lang/crates.io-index"
192 | checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
193 | dependencies = [
194 | "libc",
195 | ]
196 |
197 | [[package]]
198 | name = "fuchsia-zircon"
199 | version = "0.3.3"
200 | source = "registry+https://github.com/rust-lang/crates.io-index"
201 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
202 | dependencies = [
203 | "bitflags",
204 | "fuchsia-zircon-sys",
205 | ]
206 |
207 | [[package]]
208 | name = "fuchsia-zircon-sys"
209 | version = "0.3.3"
210 | source = "registry+https://github.com/rust-lang/crates.io-index"
211 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
212 |
213 | [[package]]
214 | name = "gimli"
215 | version = "0.27.3"
216 | source = "registry+https://github.com/rust-lang/crates.io-index"
217 | checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
218 |
219 | [[package]]
220 | name = "glob"
221 | version = "0.3.1"
222 | source = "registry+https://github.com/rust-lang/crates.io-index"
223 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
224 |
225 | [[package]]
226 | name = "hermit-abi"
227 | version = "0.3.2"
228 | source = "registry+https://github.com/rust-lang/crates.io-index"
229 | checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
230 |
231 | [[package]]
232 | name = "home"
233 | version = "0.5.5"
234 | source = "registry+https://github.com/rust-lang/crates.io-index"
235 | checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
236 | dependencies = [
237 | "windows-sys 0.48.0",
238 | ]
239 |
240 | [[package]]
241 | name = "indicatif"
242 | version = "0.16.2"
243 | source = "registry+https://github.com/rust-lang/crates.io-index"
244 | checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b"
245 | dependencies = [
246 | "console",
247 | "lazy_static",
248 | "number_prefix",
249 | "regex",
250 | ]
251 |
252 | [[package]]
253 | name = "inotify"
254 | version = "0.7.1"
255 | source = "registry+https://github.com/rust-lang/crates.io-index"
256 | checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
257 | dependencies = [
258 | "bitflags",
259 | "inotify-sys",
260 | "libc",
261 | ]
262 |
263 | [[package]]
264 | name = "inotify-sys"
265 | version = "0.1.5"
266 | source = "registry+https://github.com/rust-lang/crates.io-index"
267 | checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
268 | dependencies = [
269 | "libc",
270 | ]
271 |
272 | [[package]]
273 | name = "iovec"
274 | version = "0.1.4"
275 | source = "registry+https://github.com/rust-lang/crates.io-index"
276 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
277 | dependencies = [
278 | "libc",
279 | ]
280 |
281 | [[package]]
282 | name = "itoa"
283 | version = "1.0.8"
284 | source = "registry+https://github.com/rust-lang/crates.io-index"
285 | checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
286 |
287 | [[package]]
288 | name = "kernel32-sys"
289 | version = "0.2.2"
290 | source = "registry+https://github.com/rust-lang/crates.io-index"
291 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
292 | dependencies = [
293 | "winapi 0.2.8",
294 | "winapi-build",
295 | ]
296 |
297 | [[package]]
298 | name = "lazy_static"
299 | version = "1.4.0"
300 | source = "registry+https://github.com/rust-lang/crates.io-index"
301 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
302 |
303 | [[package]]
304 | name = "lazycell"
305 | version = "1.3.0"
306 | source = "registry+https://github.com/rust-lang/crates.io-index"
307 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
308 |
309 | [[package]]
310 | name = "libc"
311 | version = "0.2.147"
312 | source = "registry+https://github.com/rust-lang/crates.io-index"
313 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
314 |
315 | [[package]]
316 | name = "lock_api"
317 | version = "0.4.10"
318 | source = "registry+https://github.com/rust-lang/crates.io-index"
319 | checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
320 | dependencies = [
321 | "autocfg",
322 | "scopeguard",
323 | ]
324 |
325 | [[package]]
326 | name = "log"
327 | version = "0.4.19"
328 | source = "registry+https://github.com/rust-lang/crates.io-index"
329 | checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
330 |
331 | [[package]]
332 | name = "memchr"
333 | version = "2.5.0"
334 | source = "registry+https://github.com/rust-lang/crates.io-index"
335 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
336 |
337 | [[package]]
338 | name = "miniz_oxide"
339 | version = "0.7.1"
340 | source = "registry+https://github.com/rust-lang/crates.io-index"
341 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
342 | dependencies = [
343 | "adler",
344 | ]
345 |
346 | [[package]]
347 | name = "mio"
348 | version = "0.6.23"
349 | source = "registry+https://github.com/rust-lang/crates.io-index"
350 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
351 | dependencies = [
352 | "cfg-if 0.1.10",
353 | "fuchsia-zircon",
354 | "fuchsia-zircon-sys",
355 | "iovec",
356 | "kernel32-sys",
357 | "libc",
358 | "log",
359 | "miow",
360 | "net2",
361 | "slab",
362 | "winapi 0.2.8",
363 | ]
364 |
365 | [[package]]
366 | name = "mio"
367 | version = "0.8.8"
368 | source = "registry+https://github.com/rust-lang/crates.io-index"
369 | checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
370 | dependencies = [
371 | "libc",
372 | "wasi",
373 | "windows-sys 0.48.0",
374 | ]
375 |
376 | [[package]]
377 | name = "mio-extras"
378 | version = "2.0.6"
379 | source = "registry+https://github.com/rust-lang/crates.io-index"
380 | checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
381 | dependencies = [
382 | "lazycell",
383 | "log",
384 | "mio 0.6.23",
385 | "slab",
386 | ]
387 |
388 | [[package]]
389 | name = "miow"
390 | version = "0.2.2"
391 | source = "registry+https://github.com/rust-lang/crates.io-index"
392 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
393 | dependencies = [
394 | "kernel32-sys",
395 | "net2",
396 | "winapi 0.2.8",
397 | "ws2_32-sys",
398 | ]
399 |
400 | [[package]]
401 | name = "net2"
402 | version = "0.2.39"
403 | source = "registry+https://github.com/rust-lang/crates.io-index"
404 | checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac"
405 | dependencies = [
406 | "cfg-if 0.1.10",
407 | "libc",
408 | "winapi 0.3.9",
409 | ]
410 |
411 | [[package]]
412 | name = "normalize-line-endings"
413 | version = "0.3.0"
414 | source = "registry+https://github.com/rust-lang/crates.io-index"
415 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
416 |
417 | [[package]]
418 | name = "notify"
419 | version = "4.0.17"
420 | source = "registry+https://github.com/rust-lang/crates.io-index"
421 | checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257"
422 | dependencies = [
423 | "bitflags",
424 | "filetime",
425 | "fsevent",
426 | "fsevent-sys",
427 | "inotify",
428 | "libc",
429 | "mio 0.6.23",
430 | "mio-extras",
431 | "walkdir",
432 | "winapi 0.3.9",
433 | ]
434 |
435 | [[package]]
436 | name = "num-traits"
437 | version = "0.2.15"
438 | source = "registry+https://github.com/rust-lang/crates.io-index"
439 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
440 | dependencies = [
441 | "autocfg",
442 | ]
443 |
444 | [[package]]
445 | name = "num_cpus"
446 | version = "1.16.0"
447 | source = "registry+https://github.com/rust-lang/crates.io-index"
448 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
449 | dependencies = [
450 | "hermit-abi",
451 | "libc",
452 | ]
453 |
454 | [[package]]
455 | name = "number_prefix"
456 | version = "0.4.0"
457 | source = "registry+https://github.com/rust-lang/crates.io-index"
458 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
459 |
460 | [[package]]
461 | name = "object"
462 | version = "0.31.1"
463 | source = "registry+https://github.com/rust-lang/crates.io-index"
464 | checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
465 | dependencies = [
466 | "memchr",
467 | ]
468 |
469 | [[package]]
470 | name = "parking_lot"
471 | version = "0.12.1"
472 | source = "registry+https://github.com/rust-lang/crates.io-index"
473 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
474 | dependencies = [
475 | "lock_api",
476 | "parking_lot_core",
477 | ]
478 |
479 | [[package]]
480 | name = "parking_lot_core"
481 | version = "0.9.8"
482 | source = "registry+https://github.com/rust-lang/crates.io-index"
483 | checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
484 | dependencies = [
485 | "cfg-if 1.0.0",
486 | "libc",
487 | "redox_syscall 0.3.5",
488 | "smallvec",
489 | "windows-targets 0.48.1",
490 | ]
491 |
492 | [[package]]
493 | name = "pin-project-lite"
494 | version = "0.2.10"
495 | source = "registry+https://github.com/rust-lang/crates.io-index"
496 | checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
497 |
498 | [[package]]
499 | name = "predicates"
500 | version = "1.0.8"
501 | source = "registry+https://github.com/rust-lang/crates.io-index"
502 | checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df"
503 | dependencies = [
504 | "difference",
505 | "float-cmp",
506 | "normalize-line-endings",
507 | "predicates-core",
508 | "regex",
509 | ]
510 |
511 | [[package]]
512 | name = "predicates-core"
513 | version = "1.0.6"
514 | source = "registry+https://github.com/rust-lang/crates.io-index"
515 | checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
516 |
517 | [[package]]
518 | name = "predicates-tree"
519 | version = "1.0.9"
520 | source = "registry+https://github.com/rust-lang/crates.io-index"
521 | checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
522 | dependencies = [
523 | "predicates-core",
524 | "termtree",
525 | ]
526 |
527 | [[package]]
528 | name = "proc-macro2"
529 | version = "1.0.64"
530 | source = "registry+https://github.com/rust-lang/crates.io-index"
531 | checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
532 | dependencies = [
533 | "unicode-ident",
534 | ]
535 |
536 | [[package]]
537 | name = "quote"
538 | version = "1.0.29"
539 | source = "registry+https://github.com/rust-lang/crates.io-index"
540 | checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
541 | dependencies = [
542 | "proc-macro2",
543 | ]
544 |
545 | [[package]]
546 | name = "redox_syscall"
547 | version = "0.2.16"
548 | source = "registry+https://github.com/rust-lang/crates.io-index"
549 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
550 | dependencies = [
551 | "bitflags",
552 | ]
553 |
554 | [[package]]
555 | name = "redox_syscall"
556 | version = "0.3.5"
557 | source = "registry+https://github.com/rust-lang/crates.io-index"
558 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
559 | dependencies = [
560 | "bitflags",
561 | ]
562 |
563 | [[package]]
564 | name = "regex"
565 | version = "1.9.1"
566 | source = "registry+https://github.com/rust-lang/crates.io-index"
567 | checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
568 | dependencies = [
569 | "aho-corasick",
570 | "memchr",
571 | "regex-automata",
572 | "regex-syntax",
573 | ]
574 |
575 | [[package]]
576 | name = "regex-automata"
577 | version = "0.3.3"
578 | source = "registry+https://github.com/rust-lang/crates.io-index"
579 | checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
580 | dependencies = [
581 | "aho-corasick",
582 | "memchr",
583 | "regex-syntax",
584 | ]
585 |
586 | [[package]]
587 | name = "regex-syntax"
588 | version = "0.7.4"
589 | source = "registry+https://github.com/rust-lang/crates.io-index"
590 | checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
591 |
592 | [[package]]
593 | name = "rustc-demangle"
594 | version = "0.1.23"
595 | source = "registry+https://github.com/rust-lang/crates.io-index"
596 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
597 |
598 | [[package]]
599 | name = "rustlings"
600 | version = "5.5.1"
601 | dependencies = [
602 | "argh",
603 | "assert_cmd",
604 | "console",
605 | "glob",
606 | "home",
607 | "indicatif",
608 | "notify",
609 | "predicates",
610 | "regex",
611 | "serde",
612 | "serde_json",
613 | "tokio",
614 | "toml",
615 | ]
616 |
617 | [[package]]
618 | name = "ryu"
619 | version = "1.0.14"
620 | source = "registry+https://github.com/rust-lang/crates.io-index"
621 | checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
622 |
623 | [[package]]
624 | name = "same-file"
625 | version = "1.0.6"
626 | source = "registry+https://github.com/rust-lang/crates.io-index"
627 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
628 | dependencies = [
629 | "winapi-util",
630 | ]
631 |
632 | [[package]]
633 | name = "scopeguard"
634 | version = "1.2.0"
635 | source = "registry+https://github.com/rust-lang/crates.io-index"
636 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
637 |
638 | [[package]]
639 | name = "serde"
640 | version = "1.0.171"
641 | source = "registry+https://github.com/rust-lang/crates.io-index"
642 | checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
643 | dependencies = [
644 | "serde_derive",
645 | ]
646 |
647 | [[package]]
648 | name = "serde_derive"
649 | version = "1.0.171"
650 | source = "registry+https://github.com/rust-lang/crates.io-index"
651 | checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
652 | dependencies = [
653 | "proc-macro2",
654 | "quote",
655 | "syn 2.0.25",
656 | ]
657 |
658 | [[package]]
659 | name = "serde_json"
660 | version = "1.0.102"
661 | source = "registry+https://github.com/rust-lang/crates.io-index"
662 | checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed"
663 | dependencies = [
664 | "itoa",
665 | "ryu",
666 | "serde",
667 | ]
668 |
669 | [[package]]
670 | name = "signal-hook-registry"
671 | version = "1.4.1"
672 | source = "registry+https://github.com/rust-lang/crates.io-index"
673 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
674 | dependencies = [
675 | "libc",
676 | ]
677 |
678 | [[package]]
679 | name = "slab"
680 | version = "0.4.8"
681 | source = "registry+https://github.com/rust-lang/crates.io-index"
682 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
683 | dependencies = [
684 | "autocfg",
685 | ]
686 |
687 | [[package]]
688 | name = "smallvec"
689 | version = "1.11.0"
690 | source = "registry+https://github.com/rust-lang/crates.io-index"
691 | checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
692 |
693 | [[package]]
694 | name = "socket2"
695 | version = "0.4.9"
696 | source = "registry+https://github.com/rust-lang/crates.io-index"
697 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
698 | dependencies = [
699 | "libc",
700 | "winapi 0.3.9",
701 | ]
702 |
703 | [[package]]
704 | name = "syn"
705 | version = "1.0.109"
706 | source = "registry+https://github.com/rust-lang/crates.io-index"
707 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
708 | dependencies = [
709 | "proc-macro2",
710 | "quote",
711 | "unicode-ident",
712 | ]
713 |
714 | [[package]]
715 | name = "syn"
716 | version = "2.0.25"
717 | source = "registry+https://github.com/rust-lang/crates.io-index"
718 | checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2"
719 | dependencies = [
720 | "proc-macro2",
721 | "quote",
722 | "unicode-ident",
723 | ]
724 |
725 | [[package]]
726 | name = "termtree"
727 | version = "0.4.1"
728 | source = "registry+https://github.com/rust-lang/crates.io-index"
729 | checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
730 |
731 | [[package]]
732 | name = "tokio"
733 | version = "1.29.1"
734 | source = "registry+https://github.com/rust-lang/crates.io-index"
735 | checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
736 | dependencies = [
737 | "autocfg",
738 | "backtrace",
739 | "bytes",
740 | "libc",
741 | "mio 0.8.8",
742 | "num_cpus",
743 | "parking_lot",
744 | "pin-project-lite",
745 | "signal-hook-registry",
746 | "socket2",
747 | "tokio-macros",
748 | "windows-sys 0.48.0",
749 | ]
750 |
751 | [[package]]
752 | name = "tokio-macros"
753 | version = "2.1.0"
754 | source = "registry+https://github.com/rust-lang/crates.io-index"
755 | checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
756 | dependencies = [
757 | "proc-macro2",
758 | "quote",
759 | "syn 2.0.25",
760 | ]
761 |
762 | [[package]]
763 | name = "toml"
764 | version = "0.5.11"
765 | source = "registry+https://github.com/rust-lang/crates.io-index"
766 | checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
767 | dependencies = [
768 | "serde",
769 | ]
770 |
771 | [[package]]
772 | name = "unicode-ident"
773 | version = "1.0.10"
774 | source = "registry+https://github.com/rust-lang/crates.io-index"
775 | checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
776 |
777 | [[package]]
778 | name = "unicode-width"
779 | version = "0.1.10"
780 | source = "registry+https://github.com/rust-lang/crates.io-index"
781 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
782 |
783 | [[package]]
784 | name = "walkdir"
785 | version = "2.3.3"
786 | source = "registry+https://github.com/rust-lang/crates.io-index"
787 | checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
788 | dependencies = [
789 | "same-file",
790 | "winapi-util",
791 | ]
792 |
793 | [[package]]
794 | name = "wasi"
795 | version = "0.11.0+wasi-snapshot-preview1"
796 | source = "registry+https://github.com/rust-lang/crates.io-index"
797 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
798 |
799 | [[package]]
800 | name = "winapi"
801 | version = "0.2.8"
802 | source = "registry+https://github.com/rust-lang/crates.io-index"
803 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
804 |
805 | [[package]]
806 | name = "winapi"
807 | version = "0.3.9"
808 | source = "registry+https://github.com/rust-lang/crates.io-index"
809 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
810 | dependencies = [
811 | "winapi-i686-pc-windows-gnu",
812 | "winapi-x86_64-pc-windows-gnu",
813 | ]
814 |
815 | [[package]]
816 | name = "winapi-build"
817 | version = "0.1.1"
818 | source = "registry+https://github.com/rust-lang/crates.io-index"
819 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
820 |
821 | [[package]]
822 | name = "winapi-i686-pc-windows-gnu"
823 | version = "0.4.0"
824 | source = "registry+https://github.com/rust-lang/crates.io-index"
825 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
826 |
827 | [[package]]
828 | name = "winapi-util"
829 | version = "0.1.5"
830 | source = "registry+https://github.com/rust-lang/crates.io-index"
831 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
832 | dependencies = [
833 | "winapi 0.3.9",
834 | ]
835 |
836 | [[package]]
837 | name = "winapi-x86_64-pc-windows-gnu"
838 | version = "0.4.0"
839 | source = "registry+https://github.com/rust-lang/crates.io-index"
840 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
841 |
842 | [[package]]
843 | name = "windows-sys"
844 | version = "0.45.0"
845 | source = "registry+https://github.com/rust-lang/crates.io-index"
846 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
847 | dependencies = [
848 | "windows-targets 0.42.2",
849 | ]
850 |
851 | [[package]]
852 | name = "windows-sys"
853 | version = "0.48.0"
854 | source = "registry+https://github.com/rust-lang/crates.io-index"
855 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
856 | dependencies = [
857 | "windows-targets 0.48.1",
858 | ]
859 |
860 | [[package]]
861 | name = "windows-targets"
862 | version = "0.42.2"
863 | source = "registry+https://github.com/rust-lang/crates.io-index"
864 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
865 | dependencies = [
866 | "windows_aarch64_gnullvm 0.42.2",
867 | "windows_aarch64_msvc 0.42.2",
868 | "windows_i686_gnu 0.42.2",
869 | "windows_i686_msvc 0.42.2",
870 | "windows_x86_64_gnu 0.42.2",
871 | "windows_x86_64_gnullvm 0.42.2",
872 | "windows_x86_64_msvc 0.42.2",
873 | ]
874 |
875 | [[package]]
876 | name = "windows-targets"
877 | version = "0.48.1"
878 | source = "registry+https://github.com/rust-lang/crates.io-index"
879 | checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
880 | dependencies = [
881 | "windows_aarch64_gnullvm 0.48.0",
882 | "windows_aarch64_msvc 0.48.0",
883 | "windows_i686_gnu 0.48.0",
884 | "windows_i686_msvc 0.48.0",
885 | "windows_x86_64_gnu 0.48.0",
886 | "windows_x86_64_gnullvm 0.48.0",
887 | "windows_x86_64_msvc 0.48.0",
888 | ]
889 |
890 | [[package]]
891 | name = "windows_aarch64_gnullvm"
892 | version = "0.42.2"
893 | source = "registry+https://github.com/rust-lang/crates.io-index"
894 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
895 |
896 | [[package]]
897 | name = "windows_aarch64_gnullvm"
898 | version = "0.48.0"
899 | source = "registry+https://github.com/rust-lang/crates.io-index"
900 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
901 |
902 | [[package]]
903 | name = "windows_aarch64_msvc"
904 | version = "0.42.2"
905 | source = "registry+https://github.com/rust-lang/crates.io-index"
906 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
907 |
908 | [[package]]
909 | name = "windows_aarch64_msvc"
910 | version = "0.48.0"
911 | source = "registry+https://github.com/rust-lang/crates.io-index"
912 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
913 |
914 | [[package]]
915 | name = "windows_i686_gnu"
916 | version = "0.42.2"
917 | source = "registry+https://github.com/rust-lang/crates.io-index"
918 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
919 |
920 | [[package]]
921 | name = "windows_i686_gnu"
922 | version = "0.48.0"
923 | source = "registry+https://github.com/rust-lang/crates.io-index"
924 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
925 |
926 | [[package]]
927 | name = "windows_i686_msvc"
928 | version = "0.42.2"
929 | source = "registry+https://github.com/rust-lang/crates.io-index"
930 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
931 |
932 | [[package]]
933 | name = "windows_i686_msvc"
934 | version = "0.48.0"
935 | source = "registry+https://github.com/rust-lang/crates.io-index"
936 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
937 |
938 | [[package]]
939 | name = "windows_x86_64_gnu"
940 | version = "0.42.2"
941 | source = "registry+https://github.com/rust-lang/crates.io-index"
942 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
943 |
944 | [[package]]
945 | name = "windows_x86_64_gnu"
946 | version = "0.48.0"
947 | source = "registry+https://github.com/rust-lang/crates.io-index"
948 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
949 |
950 | [[package]]
951 | name = "windows_x86_64_gnullvm"
952 | version = "0.42.2"
953 | source = "registry+https://github.com/rust-lang/crates.io-index"
954 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
955 |
956 | [[package]]
957 | name = "windows_x86_64_gnullvm"
958 | version = "0.48.0"
959 | source = "registry+https://github.com/rust-lang/crates.io-index"
960 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
961 |
962 | [[package]]
963 | name = "windows_x86_64_msvc"
964 | version = "0.42.2"
965 | source = "registry+https://github.com/rust-lang/crates.io-index"
966 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
967 |
968 | [[package]]
969 | name = "windows_x86_64_msvc"
970 | version = "0.48.0"
971 | source = "registry+https://github.com/rust-lang/crates.io-index"
972 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
973 |
974 | [[package]]
975 | name = "ws2_32-sys"
976 | version = "0.2.1"
977 | source = "registry+https://github.com/rust-lang/crates.io-index"
978 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
979 | dependencies = [
980 | "winapi 0.2.8",
981 | "winapi-build",
982 | ]
983 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rustlings"
3 | description = "Small exercises to get you used to reading and writing Rust code!"
4 | version = "5.5.1"
5 | authors = [
6 | "Liv ",
7 | "Carol (Nichols || Goulding) ",
8 | ]
9 | edition = "2021"
10 |
11 | [dependencies]
12 | argh = "0.1"
13 | indicatif = "0.16"
14 | console = "0.15"
15 | notify = "4.0"
16 | toml = "0.5"
17 | regex = "1.5"
18 | serde = { version = "1.0", features = ["derive"] }
19 | serde_json = "1.0.81"
20 | home = "0.5.3"
21 | glob = "0.3.0"
22 | tokio = { version = "1.21.2", features = ["full"] }
23 |
24 | [[bin]]
25 | name = "rustlings"
26 | path = "src/main.rs"
27 |
28 | [dev-dependencies]
29 | assert_cmd = "0.11.0"
30 | predicates = "1.0.1"
31 | glob = "0.3.0"
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Carol (Nichols || Goulding)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 2025年春季第一期 Rust 入门训练营-导学阶段基础测试
2 |
3 | **请在完成本实验后凭借排行榜分数截图练习班主任加入学习交流群**
4 |
5 | 导学阶段将通过 Rustlings 进行测试,我们选取了 Rustlings 练习的前16道基础题目进行测试,确保您具备基本的编程能力,请按照以下步骤进行练习:
6 |
7 | 1. 在网络浏览器中用自己的 github id 登录 github.com。
8 | 2. 请将本仓库 fork 到您的 github 账号下,然后参照以下步骤完成环境配置和实验提交:
9 | * 本地环境:
10 | 1. **安装Linux的环境**。对于windows的用户,推荐使用wsl2安装Ubuntu 22.04,也可以使用vmware等虚拟机进行安装。如果在这一步存在问题,请联系助教。
11 | 2. **创建ssh key,用于ssh方式克隆github代码**。在linux环境下,使用`ssh-keygen -t rsa -b 4096 -C "你的邮箱"`命令,创建ssh key,下面的选项全部直接敲回车即可。 随后使用` cat ~/.ssh/id_rsa.pub` 命令查看生成的公钥,并完整的复制下来。 在github仓库界面点击自己的头像,选择`settings`。进入到设置页面后,点击左侧的`SSH and GPG keys`选项。点击`New SSH key`选项,并将复制下来的内容粘贴上去,添加该ssh key的描述。随后点击`Add SSH key`,并一路点击确认即可。
12 | 3. **本地安装rust**。进入linux环境下,参考Arceos 教程 [Rust 开发环境配置 - ArceOS Tutorial Book (rcore-os.cn)](https://rcore-os.cn/arceos-tutorial-book/ch01-02.html) 中,找到Rust 开发环境配置的章节,相应配置即可,你可以同时将后续需要的环境也配置好.
13 | 4. **clone实验仓库到本地。**在前面点击链接生成的仓库中,同样点击醒目的 `code` 绿色按钮,选择`local`下的`ssh`选项,复制下面的链接。随后回到本地linux环境下,使用`git clone 复制的链接`的方式,将目标仓库clone到本地。随后,使用`ls`命令查看自己clone下来的文件夹,再使用`cd`命令进入到该文件夹下,使用 `cargo install --force --path .` 安装rustlings。
14 | 5. **练习rustlings**。使用vscode等编辑器,进入clone下来的目录下的`exercises`文件夹,执行`rustlings watch`依次查看完成情况,并依次完成对应的练习。 执行`rustlings run 练习名称`去运行对应练习,也可以使用`rustlings hint 练习名称`查看题解。
15 | 6. **提交完成情况**。当做完部分或所有练习之后,在rustlings目录下执行 `git add .; git commit -m "update"; git push` 命令,把更新提交到GithubClassroom的CI进行自动评测。你可以在github仓库页面的actions页面,看到你的CI提交结果,或者 https://opencamp.ai/Rust/camp/S01/stage/0?tab=rank 上面查看自己的评分。
16 | * 在线环境:
17 |
18 | 1. 如果使用在线环境,在本网页的中上部可以看到一个醒目的 `code` 绿色按钮,点击后,可以进一步看到 `codespace` 标签和醒目的 `create codesapce on main` 绿色按钮。请点击这个绿色按钮,就可以进入到在线的ubuntu +vscode环境中
19 |
20 | 1. 再按照下面的环境安装提示在vscode的 `console` 中安装配置开发环境:rustc等工具。
21 |
22 | 3. 然后就可以基于在线vscode进行测试 (执行命令 `rustlings watch` ),编辑代码的循环实验过程了。
23 |
24 | 3. 上述步骤有任何问题都可以找助教。
25 |
26 | 4. 下面是官方的Rustlings的布置,可以参考,**请务必不要拉取下面的仓库!**
27 |
28 | # rustlings 🦀❤️
29 |
30 |
31 |
32 | Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages!
33 |
34 | _...looking for the old, web-based version of Rustlings? Try [here](https://github.com/rust-lang/rustlings/tree/rustlings-1)_
35 |
36 | Alternatively, for a first-time Rust learner, there are several other resources:
37 |
38 | - [The Book](https://doc.rust-lang.org/book/index.html) - The most comprehensive resource for learning Rust, but a bit theoretical sometimes. You will be using this along with Rustlings!
39 | - [Rust By Example](https://doc.rust-lang.org/rust-by-example/index.html) - Learn Rust by solving little exercises! It's almost like `rustlings`, but online
40 |
41 | ## Getting Started
42 |
43 | _Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._
44 | _Note: If you're on Linux, make sure you've installed gcc. Deb: `sudo apt install gcc`. Yum: `sudo yum -y install gcc`._
45 |
46 | You will need to have Rust installed. You can get it by visiting https://rustup.rs. This'll also install Cargo, Rust's package/project manager.
47 |
48 | ## MacOS/Linux
49 |
50 | Just run:
51 |
52 | ```bash
53 | curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash
54 | ```
55 | Or if you want it to be installed to a different path:
56 |
57 | ```bash
58 | curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -s mypath/
59 | ```
60 |
61 | This will install Rustlings and give you access to the `rustlings` command. Run it to get started!
62 |
63 | ### Nix
64 |
65 | Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`.
66 |
67 | ```bash
68 | # find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1)
69 | git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings
70 | cd rustlings
71 | # if nix version > 2.3
72 | nix develop
73 | # if nix version <= 2.3
74 | nix-shell
75 | ```
76 |
77 | ## Windows
78 |
79 | In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`:
80 |
81 | ```ps1
82 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
83 | ```
84 |
85 | Then, you can run:
86 |
87 | ```ps1
88 | 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
89 | ```
90 |
91 | To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. Keep in mind that this works best in PowerShell, and any other terminals may give you errors.
92 |
93 | If you get a permission denied message, you might have to exclude the directory where you cloned Rustlings in your antivirus.
94 |
95 | ## Browser
96 |
97 | [](https://gitpod.io/#https://github.com/rust-lang/rustlings)
98 |
99 | [](https://github.com/codespaces/new/?repo=rust-lang%2Frustlings&ref=main)
100 |
101 | ## Manually
102 |
103 | Basically: Clone the repository at the latest tag, run `cargo install --path .`.
104 |
105 | ```bash
106 | # find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1)
107 | git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings
108 | cd rustlings
109 | cargo install --force --path .
110 | ```
111 |
112 | If there are installation errors, ensure that your toolchain is up to date. For the latest, run:
113 |
114 | ```bash
115 | rustup update
116 | ```
117 |
118 | Then, same as above, run `rustlings` to get started.
119 |
120 | ## Doing exercises
121 |
122 | The exercises are sorted by topic and can be found in the subdirectory `rustlings/exercises/`. For every topic there is an additional README file with some resources to get you started on the topic. We really recommend that you have a look at them before you start.
123 |
124 | The task is simple. Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! Some exercises are also run as tests, but rustlings handles them all the same. To run the exercises in the recommended order, execute:
125 |
126 | ```bash
127 | rustlings watch
128 | ```
129 |
130 | This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. If you want to only run it once, you can use:
131 |
132 | ```bash
133 | rustlings verify
134 | ```
135 |
136 | This will do the same as watch, but it'll quit after running.
137 |
138 | In case you want to go by your own order, or want to only verify a single exercise, you can run:
139 |
140 | ```bash
141 | rustlings run myExercise1
142 | ```
143 |
144 | Or simply use the following command to run the next unsolved exercise in the course:
145 |
146 | ```bash
147 | rustlings run next
148 | ```
149 |
150 | In case you get stuck, you can run the following command to get a hint for your
151 | exercise:
152 |
153 | ```bash
154 | rustlings hint myExercise1
155 | ```
156 |
157 | You can also get the hint for the next unsolved exercise with the following command:
158 |
159 | ```bash
160 | rustlings hint next
161 | ```
162 |
163 | To check your progress, you can run the following command:
164 |
165 | ```bash
166 | rustlings list
167 | ```
168 |
169 | ## Testing yourself
170 |
171 | After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. These quizzes are found in `exercises/quizN.rs`.
172 |
173 | ## Enabling `rust-analyzer`
174 |
175 | Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise.
176 |
177 | ## Continuing On
178 |
179 | Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to.
180 |
181 | ## Uninstalling Rustlings
182 |
183 | If you want to remove Rustlings from your system, there are two steps. First, you'll need to remove the exercises folder that the install script created
184 | for you:
185 |
186 | ```bash
187 | rm -rf rustlings # or your custom folder name, if you chose and or renamed it
188 | ```
189 |
190 | Second, run `cargo uninstall` to remove the `rustlings` binary:
191 |
192 | ```bash
193 | cargo uninstall rustlings
194 | ```
195 |
196 | Now you should be done!
197 |
198 | ## Contributing
199 |
200 | See [CONTRIBUTING.md](./CONTRIBUTING.md).
201 |
202 | Development-focused discussion about Rustlings happens in the [**rustlings** stream](https://rust-lang.zulipchat.com/#narrow/stream/334454-rustlings)
203 | on the [Rust Project Zulip](https://rust-lang.zulipchat.com). Feel free to start a new thread there
204 | if you have ideas or suggestions!
205 |
206 | ## Contributors ✨
207 |
208 | Thanks goes to the wonderful people listed in [AUTHORS.md](./AUTHORS.md) 🎉
209 |
--------------------------------------------------------------------------------
/exercises/README.md:
--------------------------------------------------------------------------------
1 | # Exercise to Book Chapter mapping
2 |
3 | | Exercise | Book Chapter |
4 | | ---------------------- | ------------------- |
5 | | variables | §3.1 |
6 | | functions | §3.3 |
7 | | if | §3.5 |
8 | | primitive_types | §3.2, §4.3 |
9 | | vecs | §8.1 |
10 | | move_semantics | §4.1-2 |
11 | | structs | §5.1, §5.3 |
12 | | enums | §6, §18.3 |
13 | | strings | §8.2 |
14 | | modules | §7 |
15 | | hashmaps | §8.3 |
16 | | options | §10.1 |
17 | | error_handling | §9 |
18 | | generics | §10 |
19 | | traits | §10.2 |
20 | | tests | §11.1 |
21 | | lifetimes | §10.3 |
22 | | iterators | §13.2-4 |
23 | | threads | §16.1-3 |
24 | | smart_pointers | §15, §16.3 |
25 | | macros | §19.6 |
26 | | clippy | §21.4 |
27 | | conversions | n/a |
28 |
--------------------------------------------------------------------------------
/exercises/functions/README.md:
--------------------------------------------------------------------------------
1 | # Functions
2 |
3 | Here, you'll learn how to write functions and how the Rust compiler can help you debug errors even
4 | in more complex code.
5 |
6 | ## Further information
7 |
8 | - [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html)
9 |
--------------------------------------------------------------------------------
/exercises/functions/functions1.rs:
--------------------------------------------------------------------------------
1 | // functions1.rs
2 | //
3 | // Execute `rustlings hint functions1` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | fn main() {
9 | call_me();
10 | }
11 |
--------------------------------------------------------------------------------
/exercises/functions/functions2.rs:
--------------------------------------------------------------------------------
1 | // functions2.rs
2 | //
3 | // Execute `rustlings hint functions2` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | fn main() {
9 | call_me(3);
10 | }
11 |
12 | fn call_me(num:) {
13 | for i in 0..num {
14 | println!("Ring! Call number {}", i + 1);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/exercises/functions/functions3.rs:
--------------------------------------------------------------------------------
1 | // functions3.rs
2 | //
3 | // Execute `rustlings hint functions3` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | fn main() {
9 | call_me();
10 | }
11 |
12 | fn call_me(num: u32) {
13 | for i in 0..num {
14 | println!("Ring! Call number {}", i + 1);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/exercises/functions/functions4.rs:
--------------------------------------------------------------------------------
1 | // functions4.rs
2 | //
3 | // This store is having a sale where if the price is an even number, you get 10
4 | // Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. (Don't worry
5 | // about the function bodies themselves, we're only interested in the signatures
6 | // for now. If anything, this is a good way to peek ahead to future exercises!)
7 | //
8 | // Execute `rustlings hint functions4` or use the `hint` watch subcommand for a
9 | // hint.
10 |
11 | // I AM NOT DONE
12 |
13 | fn main() {
14 | let original_price = 51;
15 | println!("Your sale price is {}", sale_price(original_price));
16 | }
17 |
18 | fn sale_price(price: i32) -> {
19 | if is_even(price) {
20 | price - 10
21 | } else {
22 | price - 3
23 | }
24 | }
25 |
26 | fn is_even(num: i32) -> bool {
27 | num % 2 == 0
28 | }
29 |
--------------------------------------------------------------------------------
/exercises/functions/functions5.rs:
--------------------------------------------------------------------------------
1 | // functions5.rs
2 | //
3 | // Execute `rustlings hint functions5` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | fn main() {
9 | let answer = square(3);
10 | println!("The square of 3 is {}", answer);
11 | }
12 |
13 | fn square(num: i32) -> i32 {
14 | num * num;
15 | }
16 |
--------------------------------------------------------------------------------
/exercises/if/README.md:
--------------------------------------------------------------------------------
1 | # If
2 |
3 | `if`, the most basic (but still surprisingly versatile!) type of control flow, is what you'll learn here.
4 |
5 | ## Further information
6 |
7 | - [Control Flow - if expressions](https://doc.rust-lang.org/book/ch03-05-control-flow.html#if-expressions)
8 |
--------------------------------------------------------------------------------
/exercises/if/if1.rs:
--------------------------------------------------------------------------------
1 | // if1.rs
2 | //
3 | // Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint.
4 |
5 | // I AM NOT DONE
6 |
7 | pub fn bigger(a: i32, b: i32) -> i32 {
8 | // Complete this function to return the bigger number!
9 | // Do not use:
10 | // - another function call
11 | // - additional variables
12 | }
13 |
14 | // Don't mind this for now :)
15 | #[cfg(test)]
16 | mod tests {
17 | use super::*;
18 |
19 | #[test]
20 | fn ten_is_bigger_than_eight() {
21 | assert_eq!(10, bigger(10, 8));
22 | }
23 |
24 | #[test]
25 | fn fortytwo_is_bigger_than_thirtytwo() {
26 | assert_eq!(42, bigger(32, 42));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/exercises/if/if2.rs:
--------------------------------------------------------------------------------
1 | // if2.rs
2 | //
3 | // Step 1: Make me compile!
4 | // Step 2: Get the bar_for_fuzz and default_to_baz tests passing!
5 | //
6 | // Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint.
7 |
8 | // I AM NOT DONE
9 |
10 | pub fn foo_if_fizz(fizzish: &str) -> &str {
11 | if fizzish == "fizz" {
12 | "foo"
13 | } else {
14 | 1
15 | }
16 | }
17 |
18 | // No test changes needed!
19 | #[cfg(test)]
20 | mod tests {
21 | use super::*;
22 |
23 | #[test]
24 | fn foo_for_fizz() {
25 | assert_eq!(foo_if_fizz("fizz"), "foo")
26 | }
27 |
28 | #[test]
29 | fn bar_for_fuzz() {
30 | assert_eq!(foo_if_fizz("fuzz"), "bar")
31 | }
32 |
33 | #[test]
34 | fn default_to_baz() {
35 | assert_eq!(foo_if_fizz("literally anything"), "baz")
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/exercises/if/if3.rs:
--------------------------------------------------------------------------------
1 | // if3.rs
2 | //
3 | // Execute `rustlings hint if3` or use the `hint` watch subcommand for a hint.
4 |
5 | // I AM NOT DONE
6 |
7 | pub fn animal_habitat(animal: &str) -> &'static str {
8 | let identifier = if animal == "crab" {
9 | 1
10 | } else if animal == "gopher" {
11 | 2.0
12 | } else if animal == "snake" {
13 | 3
14 | } else {
15 | "Unknown"
16 | };
17 |
18 | // DO NOT CHANGE THIS STATEMENT BELOW
19 | let habitat = if identifier == 1 {
20 | "Beach"
21 | } else if identifier == 2 {
22 | "Burrow"
23 | } else if identifier == 3 {
24 | "Desert"
25 | } else {
26 | "Unknown"
27 | };
28 |
29 | habitat
30 | }
31 |
32 | #[cfg(test)]
33 | mod tests {
34 | use super::*;
35 |
36 | #[test]
37 | fn gopher_lives_in_burrow() {
38 | assert_eq!(animal_habitat("gopher"), "Burrow")
39 | }
40 |
41 | #[test]
42 | fn snake_lives_in_desert() {
43 | assert_eq!(animal_habitat("snake"), "Desert")
44 | }
45 |
46 | #[test]
47 | fn crab_lives_on_beach() {
48 | assert_eq!(animal_habitat("crab"), "Beach")
49 | }
50 |
51 | #[test]
52 | fn unknown_animal() {
53 | assert_eq!(animal_habitat("dinosaur"), "Unknown")
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/exercises/intro/README.md:
--------------------------------------------------------------------------------
1 | # Intro
2 |
3 | Rust uses the `print!` and `println!` macros to print text to the console.
4 |
5 | ## Further information
6 |
7 | - [Hello World](https://doc.rust-lang.org/rust-by-example/hello.html)
8 | - [Formatted print](https://doc.rust-lang.org/rust-by-example/hello/print.html)
9 |
--------------------------------------------------------------------------------
/exercises/intro/intro1.rs:
--------------------------------------------------------------------------------
1 | // intro1.rs
2 | //
3 | // About this `I AM NOT DONE` thing:
4 | // We sometimes encourage you to keep trying things on a given exercise, even
5 | // after you already figured it out. If you got everything working and feel
6 | // ready for the next exercise, remove the `I AM NOT DONE` comment below.
7 | //
8 | // If you're running this using `rustlings watch`: The exercise file will be
9 | // reloaded when you change one of the lines below! Try adding a `println!`
10 | // line, or try changing what it outputs in your terminal. Try removing a
11 | // semicolon and see what happens!
12 | //
13 | // Execute `rustlings hint intro1` or use the `hint` watch subcommand for a
14 | // hint.
15 |
16 |
17 | fn main() {
18 | println!("Hello and");
19 | println!(r#" welcome to... "#);
20 | println!(r#" _ _ _ "#);
21 | println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
22 | println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);
23 | println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#);
24 | println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
25 | println!(r#" |___/ "#);
26 | println!();
27 | println!("This exercise compiles successfully. The remaining exercises contain a compiler");
28 | println!("or logic error. The central concept behind Rustlings is to fix these errors and");
29 | println!("solve the exercises. Good luck!");
30 | println!();
31 | println!("The source for this exercise is in `exercises/intro/intro1.rs`. Have a look!");
32 | println!(
33 | "Going forward, the source of the exercises will always be in the success/failure output."
34 | );
35 | println!();
36 | println!(
37 | "If you want to use rust-analyzer, Rust's LSP implementation, make sure your editor is set"
38 | );
39 | println!("up, and then run `rustlings lsp` before continuing.")
40 | }
41 |
--------------------------------------------------------------------------------
/exercises/intro/intro2.rs:
--------------------------------------------------------------------------------
1 | // intro2.rs
2 | //
3 | // Make the code print a greeting to the world.
4 | //
5 | // Execute `rustlings hint intro2` or use the `hint` watch subcommand for a
6 | // hint.
7 |
8 | // I AM NOT DONE
9 |
10 | fn main() {
11 | println!("Hello {}!");
12 | }
13 |
--------------------------------------------------------------------------------
/exercises/quiz1.rs:
--------------------------------------------------------------------------------
1 | // quiz1.rs
2 | //
3 | // This is a quiz for the following sections:
4 | // - Variables
5 | // - Functions
6 | // - If
7 | //
8 | // Mary is buying apples. The price of an apple is calculated as follows:
9 | // - An apple costs 2 rustbucks.
10 | // - If Mary buys more than 40 apples, each apple only costs 1 rustbuck!
11 | // Write a function that calculates the price of an order of apples given the
12 | // quantity bought. No hints this time!
13 | //
14 | // No hints this time ;)
15 |
16 | // I AM NOT DONE
17 |
18 | // Put your function here!
19 | // fn calculate_price_of_apples {
20 |
21 | // Don't modify this function!
22 | #[test]
23 | fn verify_test() {
24 | let price1 = calculate_price_of_apples(35);
25 | let price2 = calculate_price_of_apples(40);
26 | let price3 = calculate_price_of_apples(41);
27 | let price4 = calculate_price_of_apples(65);
28 |
29 | assert_eq!(70, price1);
30 | assert_eq!(80, price2);
31 | assert_eq!(41, price3);
32 | assert_eq!(65, price4);
33 | }
34 |
--------------------------------------------------------------------------------
/exercises/variables/README.md:
--------------------------------------------------------------------------------
1 | # Variables
2 |
3 | In Rust, variables are immutable by default.
4 | When a variable is immutable, once a value is bound to a name, you can’t change that value.
5 | You can make them mutable by adding `mut` in front of the variable name.
6 |
7 | ## Further information
8 |
9 | - [Variables and Mutability](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html)
10 |
--------------------------------------------------------------------------------
/exercises/variables/variables1.rs:
--------------------------------------------------------------------------------
1 | // variables1.rs
2 | //
3 | // Make me compile!
4 | //
5 | // Execute `rustlings hint variables1` or use the `hint` watch subcommand for a
6 | // hint.
7 |
8 | // I AM NOT DONE
9 |
10 | fn main() {
11 | x = 5;
12 | println!("x has the value {}", x);
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/variables/variables2.rs:
--------------------------------------------------------------------------------
1 | // variables2.rs
2 | //
3 | // Execute `rustlings hint variables2` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | fn main() {
9 | let x;
10 | if x == 10 {
11 | println!("x is ten!");
12 | } else {
13 | println!("x is not ten!");
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/exercises/variables/variables3.rs:
--------------------------------------------------------------------------------
1 | // variables3.rs
2 | //
3 | // Execute `rustlings hint variables3` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | fn main() {
9 | let x: i32;
10 | println!("Number {}", x);
11 | }
12 |
--------------------------------------------------------------------------------
/exercises/variables/variables4.rs:
--------------------------------------------------------------------------------
1 | // variables4.rs
2 | //
3 | // Execute `rustlings hint variables4` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | fn main() {
9 | let x = 3;
10 | println!("Number {}", x);
11 | x = 5; // don't change this line
12 | println!("Number {}", x);
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/variables/variables5.rs:
--------------------------------------------------------------------------------
1 | // variables5.rs
2 | //
3 | // Execute `rustlings hint variables5` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | fn main() {
9 | let number = "T-H-R-E-E"; // don't change this line
10 | println!("Spell a Number : {}", number);
11 | number = 3; // don't rename this variable
12 | println!("Number plus two is : {}", number + 2);
13 | }
14 |
--------------------------------------------------------------------------------
/exercises/variables/variables6.rs:
--------------------------------------------------------------------------------
1 | // variables6.rs
2 | //
3 | // Execute `rustlings hint variables6` or use the `hint` watch subcommand for a
4 | // hint.
5 |
6 | // I AM NOT DONE
7 |
8 | const NUMBER = 3;
9 | fn main() {
10 | println!("Number {}", NUMBER);
11 | }
12 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-compat": {
4 | "flake": false,
5 | "locked": {
6 | "lastModified": 1650374568,
7 | "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
8 | "owner": "edolstra",
9 | "repo": "flake-compat",
10 | "rev": "b4a34015c698c7793d592d66adbab377907a2be8",
11 | "type": "github"
12 | },
13 | "original": {
14 | "owner": "edolstra",
15 | "repo": "flake-compat",
16 | "type": "github"
17 | }
18 | },
19 | "flake-utils": {
20 | "locked": {
21 | "lastModified": 1659877975,
22 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
23 | "owner": "numtide",
24 | "repo": "flake-utils",
25 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
26 | "type": "github"
27 | },
28 | "original": {
29 | "owner": "numtide",
30 | "repo": "flake-utils",
31 | "type": "github"
32 | }
33 | },
34 | "nixpkgs": {
35 | "locked": {
36 | "lastModified": 1666629043,
37 | "narHash": "sha256-Yoq6Ut2F3Ol73yO9hG93x6ts5c4F5BhKTbcF3DtBEAw=",
38 | "owner": "nixos",
39 | "repo": "nixpkgs",
40 | "rev": "b39fd6e4edef83cb4a135ebef98751ce23becc33",
41 | "type": "github"
42 | },
43 | "original": {
44 | "owner": "nixos",
45 | "ref": "nixos-unstable",
46 | "repo": "nixpkgs",
47 | "type": "github"
48 | }
49 | },
50 | "root": {
51 | "inputs": {
52 | "flake-compat": "flake-compat",
53 | "flake-utils": "flake-utils",
54 | "nixpkgs": "nixpkgs"
55 | }
56 | }
57 | },
58 | "root": "root",
59 | "version": 7
60 | }
61 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "Small exercises to get you used to reading and writing Rust code";
3 |
4 | inputs = {
5 | flake-compat = {
6 | url = "github:edolstra/flake-compat";
7 | flake = false;
8 | };
9 | flake-utils.url = "github:numtide/flake-utils";
10 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
11 | };
12 |
13 | outputs = { self, flake-utils, nixpkgs, ... }:
14 | flake-utils.lib.eachDefaultSystem (system:
15 | let
16 | pkgs = nixpkgs.legacyPackages.${system};
17 |
18 | cargoBuildInputs = with pkgs; lib.optionals stdenv.isDarwin [
19 | darwin.apple_sdk.frameworks.CoreServices
20 | ];
21 |
22 | rustlings =
23 | pkgs.rustPlatform.buildRustPackage {
24 | name = "rustlings";
25 | version = "5.5.1";
26 |
27 | buildInputs = cargoBuildInputs;
28 |
29 | src = with pkgs.lib; cleanSourceWith {
30 | src = self;
31 | # a function that returns a bool determining if the path should be included in the cleaned source
32 | filter = path: type:
33 | let
34 | # filename
35 | baseName = builtins.baseNameOf (toString path);
36 | # path from root directory
37 | path' = builtins.replaceStrings [ "${self}/" ] [ "" ] path;
38 | # checks if path is in the directory
39 | inDirectory = directory: hasPrefix directory path';
40 | in
41 | inDirectory "src" ||
42 | inDirectory "tests" ||
43 | hasPrefix "Cargo" baseName ||
44 | baseName == "info.toml";
45 | };
46 |
47 | cargoLock.lockFile = ./Cargo.lock;
48 | };
49 | in
50 | {
51 | devShell = pkgs.mkShell {
52 | RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
53 |
54 | buildInputs = with pkgs; [
55 | cargo
56 | rustc
57 | rust-analyzer
58 | rustlings
59 | rustfmt
60 | clippy
61 | ] ++ cargoBuildInputs;
62 | };
63 | });
64 | }
65 |
--------------------------------------------------------------------------------
/info.toml:
--------------------------------------------------------------------------------
1 | # INTRO
2 |
3 | # [[exercises]]
4 | # name = "intro1"
5 | # path = "exercises/intro/intro1.rs"
6 | # mode = "compile"
7 | # hint = """
8 | # Remove the I AM NOT DONE comment in the exercises/intro/intro1.rs file
9 | # to move on to the next exercise."""
10 |
11 | [[exercises]]
12 | name = "intro2"
13 | path = "exercises/intro/intro2.rs"
14 | mode = "compile"
15 | hint = """
16 | Add an argument after the format string."""
17 |
18 | # VARIABLES
19 |
20 | [[exercises]]
21 | name = "variables1"
22 | path = "exercises/variables/variables1.rs"
23 | mode = "compile"
24 | hint = """
25 | The declaration on line 8 is missing a keyword that is needed in Rust
26 | to create a new variable binding."""
27 |
28 | [[exercises]]
29 | name = "variables2"
30 | path = "exercises/variables/variables2.rs"
31 | mode = "compile"
32 | hint = """
33 | The compiler message is saying that Rust cannot infer the type that the
34 | variable binding `x` has with what is given here.
35 | What happens if you annotate line 7 with a type annotation?
36 | What if you give x a value?
37 | What if you do both?
38 | What type should x be, anyway?
39 | What if x is the same type as 10? What if it's a different type?"""
40 |
41 | [[exercises]]
42 | name = "variables3"
43 | path = "exercises/variables/variables3.rs"
44 | mode = "compile"
45 | hint = """
46 | Oops! In this exercise, we have a variable binding that we've created on
47 | line 7, and we're trying to use it on line 8, but we haven't given it a
48 | value. We can't print out something that isn't there; try giving x a value!
49 | This is an error that can cause bugs that's very easy to make in any
50 | programming language -- thankfully the Rust compiler has caught this for us!"""
51 |
52 | [[exercises]]
53 | name = "variables4"
54 | path = "exercises/variables/variables4.rs"
55 | mode = "compile"
56 | hint = """
57 | In Rust, variable bindings are immutable by default. But here we're trying
58 | to reassign a different value to x! There's a keyword we can use to make
59 | a variable binding mutable instead."""
60 |
61 | [[exercises]]
62 | name = "variables5"
63 | path = "exercises/variables/variables5.rs"
64 | mode = "compile"
65 | hint = """
66 | In variables4 we already learned how to make an immutable variable mutable
67 | using a special keyword. Unfortunately this doesn't help us much in this exercise
68 | because we want to assign a different typed value to an existing variable. Sometimes
69 | you may also like to reuse existing variable names because you are just converting
70 | values to different types like in this exercise.
71 | Fortunately Rust has a powerful solution to this problem: 'Shadowing'!
72 | You can read more about 'Shadowing' in the book's section 'Variables and Mutability':
73 | https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing
74 | Try to solve this exercise afterwards using this technique."""
75 |
76 | [[exercises]]
77 | name = "variables6"
78 | path = "exercises/variables/variables6.rs"
79 | mode = "compile"
80 | hint = """
81 | We know about variables and mutability, but there is another important type of
82 | variable available: constants.
83 | Constants are always immutable and they are declared with keyword 'const' rather
84 | than keyword 'let'.
85 | Constants types must also always be annotated.
86 |
87 | Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability':
88 | https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants
89 | """
90 |
91 | # FUNCTIONS
92 |
93 | [[exercises]]
94 | name = "functions1"
95 | path = "exercises/functions/functions1.rs"
96 | mode = "compile"
97 | hint = """
98 | This main function is calling a function that it expects to exist, but the
99 | function doesn't exist. It expects this function to have the name `call_me`.
100 | It expects this function to not take any arguments and not return a value.
101 | Sounds a lot like `main`, doesn't it?"""
102 |
103 | [[exercises]]
104 | name = "functions2"
105 | path = "exercises/functions/functions2.rs"
106 | mode = "compile"
107 | hint = """
108 | Rust requires that all parts of a function's signature have type annotations,
109 | but `call_me` is missing the type annotation of `num`."""
110 |
111 | [[exercises]]
112 | name = "functions3"
113 | path = "exercises/functions/functions3.rs"
114 | mode = "compile"
115 | hint = """
116 | This time, the function *declaration* is okay, but there's something wrong
117 | with the place where we're calling the function.
118 | As a reminder, you can freely play around with different solutions in Rustlings!
119 | Watch mode will only jump to the next exercise if you remove the I AM NOT DONE comment."""
120 |
121 | [[exercises]]
122 | name = "functions4"
123 | path = "exercises/functions/functions4.rs"
124 | mode = "compile"
125 | hint = """
126 | The error message points to line 17 and says it expects a type after the
127 | `->`. This is where the function's return type should be -- take a look at
128 | the `is_even` function for an example!
129 |
130 | Also: Did you figure out that, technically, u32 would be the more fitting type
131 | for the prices here, since they can't be negative? If so, kudos!"""
132 |
133 | [[exercises]]
134 | name = "functions5"
135 | path = "exercises/functions/functions5.rs"
136 | mode = "compile"
137 | hint = """
138 | This is a really common error that can be fixed by removing one character.
139 | It happens because Rust distinguishes between expressions and statements: expressions return a value based on their operand(s), and statements simply return a () type which behaves just like `void` in C/C++ language.
140 | We want to return a value of `i32` type from the `square` function, but it is returning a `()` type...
141 | They are not the same. There are two solutions:
142 | 1. Add a `return` ahead of `num * num;`
143 | 2. remove `;`, make it to be `num * num`"""
144 |
145 | # IF
146 |
147 | [[exercises]]
148 | name = "if1"
149 | path = "exercises/if/if1.rs"
150 | mode = "test"
151 | hint = """
152 | It's possible to do this in one line if you would like!
153 | Some similar examples from other languages:
154 | - In C(++) this would be: `a > b ? a : b`
155 | - In Python this would be: `a if a > b else b`
156 | Remember in Rust that:
157 | - the `if` condition does not need to be surrounded by parentheses
158 | - `if`/`else` conditionals are expressions
159 | - Each condition is followed by a `{}` block."""
160 |
161 | [[exercises]]
162 | name = "if2"
163 | path = "exercises/if/if2.rs"
164 | mode = "test"
165 | hint = """
166 | For that first compiler error, it's important in Rust that each conditional
167 | block returns the same type! To get the tests passing, you will need a couple
168 | conditions checking different input values."""
169 |
170 | [[exercises]]
171 | name = "if3"
172 | path = "exercises/if/if3.rs"
173 | mode = "test"
174 | hint = """
175 | In Rust, every arm of an `if` expression has to return the same type of value. Make sure the type is consistent across all arms."""
176 |
177 | # QUIZ 1
178 |
179 | [[exercises]]
180 | name = "quiz1"
181 | path = "exercises/quiz1.rs"
182 | mode = "test"
183 | hint = "No hints this time ;)"
--------------------------------------------------------------------------------
/install.ps1:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env pwsh
2 |
3 | #Requires -Version 5
4 | param($path = "$home/rustlings")
5 |
6 | Write-Host "Let's get you set up with Rustlings!"
7 |
8 | Write-Host "Checking requirements..."
9 | if (Get-Command git -ErrorAction SilentlyContinue) {
10 | Write-Host "SUCCESS: Git is installed"
11 | } else {
12 | Write-Host "WARNING: Git does not seem to be installed."
13 | Write-Host "Please download Git using your package manager or over https://git-scm.com/!"
14 | exit 1
15 | }
16 |
17 | if (Get-Command rustc -ErrorAction SilentlyContinue) {
18 | Write-Host "SUCCESS: Rust is installed"
19 | } else {
20 | Write-Host "WARNING: Rust does not seem to be installed."
21 | Write-Host "Please download Rust using https://rustup.rs!"
22 | exit 1
23 | }
24 |
25 | if (Get-Command cargo -ErrorAction SilentlyContinue) {
26 | Write-Host "SUCCESS: Cargo is installed"
27 | } else {
28 | Write-Host "WARNING: Cargo does not seem to be installed."
29 | Write-Host "Please download Rust and Cargo using https://rustup.rs!"
30 | exit 1
31 | }
32 |
33 | # Function that compares two versions strings v1 and v2 given in arguments (e.g 1.31 and 1.33.0).
34 | # Returns 1 if v1 > v2, 0 if v1 == v2, 2 if v1 < v2.
35 | function vercomp($v1, $v2) {
36 | if ($v1 -eq $v2) {
37 | return 0
38 | }
39 |
40 | $v1 = $v1.Replace(".", "0")
41 | $v2 = $v2.Replace(".", "0")
42 | if ($v1.Length -gt $v2.Length) {
43 | $v2 = $v2.PadRight($v1.Length, "0")
44 | } else {
45 | $v1 = $v1.PadRight($v2.Length, "0")
46 | }
47 |
48 | if ($v1 -gt $v2) {
49 | return 1
50 | } else {
51 | return 2
52 | }
53 | }
54 |
55 | $rustVersion = $(rustc --version).Split(" ")[1]
56 | $minRustVersion = "1.56"
57 | if ((vercomp $rustVersion $minRustVersion) -eq 2) {
58 | Write-Host "WARNING: Rust version is too old: $rustVersion - needs at least $minRustVersion"
59 | Write-Host "Please update Rust with 'rustup update'"
60 | exit 1
61 | } else {
62 | Write-Host "SUCCESS: Rust is up to date"
63 | }
64 |
65 | Write-Host "Cloning Rustlings at $path"
66 | git clone -q https://github.com/rust-lang/rustlings $path
67 | if (!($LASTEXITCODE -eq 0)) {
68 | exit 1
69 | }
70 |
71 | # UseBasicParsing is deprecated, pwsh 6 or above will automatically use it,
72 | # but anyone running pwsh 5 will have to pass the argument.
73 | $version = Invoke-WebRequest -UseBasicParsing https://api.github.com/repos/rust-lang/rustlings/releases/latest `
74 | | ConvertFrom-Json | Select-Object -ExpandProperty tag_name
75 |
76 | Write-Host "Checking out version $version..."
77 | Set-Location $path
78 | git checkout -q tags/$version
79 |
80 | Write-Host "Installing the 'rustlings' executable..."
81 | cargo install --force --path .
82 | if (!(Get-Command rustlings -ErrorAction SilentlyContinue)) {
83 | Write-Host "WARNING: Please check that you have '~/.cargo/bin' in your PATH environment variable!"
84 | }
85 |
86 | # Checking whether Clippy is installed.
87 | # Due to a bug in Cargo, this must be done with Rustup: https://github.com/rust-lang/rustup/issues/1514
88 | $clippy = (rustup component list | Select-String "clippy" | Select-String "installed") | Out-String
89 | if (!$clippy) {
90 | Write-Host "Installing the 'cargo-clippy' executable..."
91 | rustup component add clippy
92 | }
93 |
94 | Write-Host "All done! Navigate to $path and run 'rustlings' to get started!"
95 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | echo "Let's get you set up with Rustlings!"
5 |
6 | echo "Checking requirements..."
7 | if [ -x "$(command -v git)" ]
8 | then
9 | echo "SUCCESS: Git is installed"
10 | else
11 | echo "ERROR: Git does not seem to be installed."
12 | echo "Please download Git using your package manager or over https://git-scm.com/!"
13 | exit 1
14 | fi
15 |
16 | if [ -x "$(command -v cc)" ]
17 | then
18 | echo "SUCCESS: cc is installed"
19 | else
20 | echo "ERROR: cc does not seem to be installed."
21 | echo "Please download (g)cc using your package manager."
22 | echo "OSX: xcode-select --install"
23 | echo "Deb: sudo apt install gcc"
24 | echo "Yum: sudo yum -y install gcc"
25 | exit 1
26 | fi
27 |
28 | if [ -x "$(command -v rustup)" ]
29 | then
30 | echo "SUCCESS: rustup is installed"
31 | else
32 | echo "ERROR: rustup does not seem to be installed."
33 | echo "Please download rustup using https://rustup.rs!"
34 | exit 1
35 | fi
36 |
37 | if [ -x "$(command -v rustc)" ]
38 | then
39 | echo "SUCCESS: Rust is installed"
40 | else
41 | echo "ERROR: Rust does not seem to be installed."
42 | echo "Please download Rust using rustup!"
43 | exit 1
44 | fi
45 |
46 | if [ -x "$(command -v cargo)" ]
47 | then
48 | echo "SUCCESS: Cargo is installed"
49 | else
50 | echo "ERROR: Cargo does not seem to be installed."
51 | echo "Please download Rust and Cargo using rustup!"
52 | exit 1
53 | fi
54 |
55 | # Look up python installations, starting with 3 with a fallback of 2
56 | if [ -x "$(command -v python3)" ]
57 | then
58 | PY="$(command -v python3)"
59 | elif [ -x "$(command -v python)" ]
60 | then
61 | PY="$(command -v python)"
62 | elif [ -x "$(command -v python2)" ]
63 | then
64 | PY="$(command -v python2)"
65 | else
66 | echo "ERROR: No working python installation was found"
67 | echo "Please install python and add it to the PATH variable"
68 | exit 1
69 | fi
70 |
71 | # Function that compares two versions strings v1 and v2 given in arguments (e.g 1.31 and 1.33.0).
72 | # Returns 1 if v1 > v2, 0 if v1 == v2, 2 if v1 < v2.
73 | function vercomp() {
74 | if [[ $1 == $2 ]]
75 | then
76 | return 0
77 | fi
78 | v1=( ${1//./ } )
79 | v2=( ${2//./ } )
80 | len1=${#v1[@]}
81 | len2=${#v2[@]}
82 | max_len=$len1
83 | if [[ $max_len -lt $len2 ]]
84 | then
85 | max_len=$len2
86 | fi
87 |
88 | #pad right in short arr
89 | if [[ len1 -gt len2 ]];
90 | then
91 | for ((i = len2; i < len1; i++));
92 | do
93 | v2[$i]=0
94 | done
95 | else
96 | for ((i = len1; i < len2; i++));
97 | do
98 | v1[$i]=0
99 | done
100 | fi
101 |
102 | for i in `seq 0 $((max_len-1))`
103 | do
104 | # Fill empty fields with zeros in v1
105 | if [ -z "${v1[$i]}" ]
106 | then
107 | v1[$i]=0
108 | fi
109 | # And in v2
110 | if [ -z "${v2[$i]}" ]
111 | then
112 | v2[$i]=0
113 | fi
114 | if [ ${v1[$i]} -gt ${v2[$i]} ]
115 | then
116 | return 1
117 | fi
118 | if [ ${v1[$i]} -lt ${v2[$i]} ]
119 | then
120 | return 2
121 | fi
122 | done
123 | return 0
124 | }
125 |
126 | RustVersion=$(rustc --version | cut -d " " -f 2)
127 | MinRustVersion=1.58
128 | vercomp "$RustVersion" $MinRustVersion || ec=$?
129 | if [ ${ec:-0} -eq 2 ]
130 | then
131 | echo "ERROR: Rust version is too old: $RustVersion - needs at least $MinRustVersion"
132 | echo "Please update Rust with 'rustup update'"
133 | exit 1
134 | else
135 | echo "SUCCESS: Rust is up to date"
136 | fi
137 |
138 | Path=${1:-rustlings/}
139 | echo "Cloning Rustlings at $Path..."
140 | git clone -q https://github.com/rust-lang/rustlings "$Path"
141 |
142 | cd "$Path"
143 |
144 | Version=$(curl -s https://api.github.com/repos/rust-lang/rustlings/releases/latest | ${PY} -c "import json,sys;obj=json.load(sys.stdin);print(obj['tag_name']) if 'tag_name' in obj else sys.exit(f\"Error: {obj['message']}\");")
145 | CargoBin="${CARGO_HOME:-$HOME/.cargo}/bin"
146 |
147 | if [[ -z ${Version} ]]
148 | then
149 | echo "The latest tag version could not be fetched remotely."
150 | echo "Using the local git repository..."
151 | Version=$(ls -tr .git/refs/tags/ | tail -1)
152 | if [[ -z ${Version} ]]
153 | then
154 | echo "No valid tag version found"
155 | echo "Rustlings will be installed using the main branch"
156 | Version="main"
157 | else
158 | Version="tags/${Version}"
159 | fi
160 | else
161 | Version="tags/${Version}"
162 | fi
163 |
164 | echo "Checking out version $Version..."
165 | git checkout -q ${Version}
166 |
167 | echo "Installing the 'rustlings' executable..."
168 | cargo install --force --path .
169 |
170 | if ! [ -x "$(command -v rustlings)" ]
171 | then
172 | echo "WARNING: Please check that you have '$CargoBin' in your PATH environment variable!"
173 | fi
174 |
175 | # Checking whether Clippy is installed.
176 | # Due to a bug in Cargo, this must be done with Rustup: https://github.com/rust-lang/rustup/issues/1514
177 | Clippy=$(rustup component list | grep "clippy" | grep "installed")
178 | if [ -z "$Clippy" ]
179 | then
180 | echo "Installing the 'cargo-clippy' executable..."
181 | rustup component add clippy
182 | fi
183 |
184 | echo "All done! Run 'rustlings' to get started."
185 |
--------------------------------------------------------------------------------
/oranda.json:
--------------------------------------------------------------------------------
1 | {
2 | "project": {
3 | "homepage": "https://rustlings.cool",
4 | "repository": "https://github.com/rust-lang/rustlings"
5 | },
6 | "marketing": {
7 | "analytics": {
8 | "plausible": {
9 | "domain": "rustlings.cool"
10 | }
11 | }
12 | },
13 | "components": {
14 | "artifacts": {
15 | "cargo_dist": false,
16 | "package_managers": {
17 | "preferred": {
18 | "macos/linux/unix": "curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash",
19 | "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"
20 | }
21 | }
22 | },
23 | "changelog": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/shell.nix:
--------------------------------------------------------------------------------
1 | (import (let lock = builtins.fromJSON (builtins.readFile ./flake.lock);
2 | in fetchTarball {
3 | url =
4 | "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
5 | sha256 = lock.nodes.flake-compat.locked.narHash;
6 | }) { src = ./.; }).shellNix
7 |
--------------------------------------------------------------------------------
/src/exercise.rs:
--------------------------------------------------------------------------------
1 | use regex::Regex;
2 | use serde::Deserialize;
3 | use std::env;
4 | use std::fmt::{self, Display, Formatter};
5 | use std::fs::{self, remove_file, File};
6 | use std::io::Read;
7 | use std::path::PathBuf;
8 | use std::process::{self, Command};
9 |
10 | const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];
11 | const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"];
12 | const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE";
13 | const CONTEXT: usize = 2;
14 | const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/clippy/Cargo.toml";
15 | const BUILD_SCRIPT_CARGO_TOML_PATH: &str = "./exercises/tests/Cargo.toml";
16 |
17 | // Get a temporary file name that is hopefully unique
18 | #[inline]
19 | fn temp_file() -> String {
20 | let thread_id: String = format!("{:?}", std::thread::current().id())
21 | .chars()
22 | .filter(|c| c.is_alphanumeric())
23 | .collect();
24 |
25 | format!("./temp_{}_{thread_id}", process::id())
26 | }
27 |
28 | // The mode of the exercise.
29 | #[derive(Deserialize, Copy, Clone, Debug)]
30 | #[serde(rename_all = "lowercase")]
31 | pub enum Mode {
32 | // Indicates that the exercise should be compiled as a binary
33 | Compile,
34 | // Indicates that the exercise should be compiled as a test harness
35 | Test,
36 | // Indicates that the exercise should be linted with clippy
37 | Clippy,
38 | // Indicates that the exercise should be run using cargo with build script
39 | BuildScript,
40 | }
41 |
42 | #[derive(Deserialize)]
43 | pub struct ExerciseList {
44 | pub exercises: Vec,
45 | }
46 |
47 | // A representation of a rustlings exercise.
48 | // This is deserialized from the accompanying info.toml file
49 | #[derive(Deserialize, Debug)]
50 | pub struct Exercise {
51 | // Name of the exercise
52 | pub name: String,
53 | // The path to the file containing the exercise's source code
54 | pub path: PathBuf,
55 | // The mode of the exercise (Test, Compile, or Clippy)
56 | pub mode: Mode,
57 | // The hint text associated with the exercise
58 | pub hint: String,
59 | }
60 |
61 | // An enum to track of the state of an Exercise.
62 | // An Exercise can be either Done or Pending
63 | #[derive(PartialEq, Debug)]
64 | pub enum State {
65 | // The state of the exercise once it's been completed
66 | Done,
67 | // The state of the exercise while it's not completed yet
68 | Pending(Vec),
69 | }
70 |
71 | // The context information of a pending exercise
72 | #[derive(PartialEq, Debug)]
73 | pub struct ContextLine {
74 | // The source code that is still pending completion
75 | pub line: String,
76 | // The line number of the source code still pending completion
77 | pub number: usize,
78 | // Whether or not this is important
79 | pub important: bool,
80 | }
81 |
82 | // The result of compiling an exercise
83 | pub struct CompiledExercise<'a> {
84 | exercise: &'a Exercise,
85 | _handle: FileHandle,
86 | }
87 |
88 | impl<'a> CompiledExercise<'a> {
89 | // Run the compiled exercise
90 | pub fn run(&self) -> Result {
91 | self.exercise.run()
92 | }
93 | }
94 |
95 | // A representation of an already executed binary
96 | #[derive(Debug)]
97 | pub struct ExerciseOutput {
98 | // The textual contents of the standard output of the binary
99 | pub stdout: String,
100 | // The textual contents of the standard error of the binary
101 | pub stderr: String,
102 | }
103 |
104 | struct FileHandle;
105 |
106 | impl Drop for FileHandle {
107 | fn drop(&mut self) {
108 | clean();
109 | }
110 | }
111 |
112 | impl Exercise {
113 | pub fn compile(&self) -> Result {
114 | let cmd = match self.mode {
115 | Mode::Compile => Command::new("rustc")
116 | .args(&[self.path.to_str().unwrap(), "-o", &temp_file()])
117 | .args(RUSTC_COLOR_ARGS)
118 | .args(RUSTC_EDITION_ARGS)
119 | .output(),
120 | Mode::Test => Command::new("rustc")
121 | .args(&["--test", self.path.to_str().unwrap(), "-o", &temp_file()])
122 | .args(RUSTC_COLOR_ARGS)
123 | .args(RUSTC_EDITION_ARGS)
124 | .output(),
125 | Mode::Clippy => {
126 | let cargo_toml = format!(
127 | r#"[package]
128 | name = "{}"
129 | version = "0.0.1"
130 | edition = "2021"
131 | [[bin]]
132 | name = "{}"
133 | path = "{}.rs""#,
134 | self.name, self.name, self.name
135 | );
136 | let cargo_toml_error_msg = if env::var("NO_EMOJI").is_ok() {
137 | "Failed to write Clippy Cargo.toml file."
138 | } else {
139 | "Failed to write 📎 Clippy 📎 Cargo.toml file."
140 | };
141 | fs::write(CLIPPY_CARGO_TOML_PATH, cargo_toml).expect(cargo_toml_error_msg);
142 | // To support the ability to run the clippy exercises, build
143 | // an executable, in addition to running clippy. With a
144 | // compilation failure, this would silently fail. But we expect
145 | // clippy to reflect the same failure while compiling later.
146 | Command::new("rustc")
147 | .args(&[self.path.to_str().unwrap(), "-o", &temp_file()])
148 | .args(RUSTC_COLOR_ARGS)
149 | .args(RUSTC_EDITION_ARGS)
150 | .output()
151 | .expect("Failed to compile!");
152 | // Due to an issue with Clippy, a cargo clean is required to catch all lints.
153 | // See https://github.com/rust-lang/rust-clippy/issues/2604
154 | // This is already fixed on Clippy's master branch. See this issue to track merging into Cargo:
155 | // https://github.com/rust-lang/rust-clippy/issues/3837
156 | Command::new("cargo")
157 | .args(&["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
158 | .args(RUSTC_COLOR_ARGS)
159 | .output()
160 | .expect("Failed to run 'cargo clean'");
161 | Command::new("cargo")
162 | .args(&["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
163 | .args(RUSTC_COLOR_ARGS)
164 | .args(&["--", "-D", "warnings", "-D", "clippy::float_cmp"])
165 | .output()
166 | },
167 | Mode::BuildScript => {
168 | let cargo_toml = format!(
169 | r#"[package]
170 | name = "{}"
171 | version = "0.0.1"
172 | edition = "2021"
173 | [[bin]]
174 | name = "{}"
175 | path = "{}.rs""#,
176 | self.name, self.name, self.name
177 | );
178 | let cargo_toml_error_msg = if env::var("NO_EMOJI").is_ok() {
179 | "Failed to write Clippy Cargo.toml file."
180 | } else {
181 | "Failed to write 📎 Clippy 📎 Cargo.toml file."
182 | };
183 | fs::write(BUILD_SCRIPT_CARGO_TOML_PATH, cargo_toml).expect(cargo_toml_error_msg);
184 |
185 | Command::new("cargo")
186 | .args(&["test", "--manifest-path", BUILD_SCRIPT_CARGO_TOML_PATH])
187 | .output()
188 | }
189 | }
190 | .expect("Failed to run 'compile' command.");
191 |
192 | if cmd.status.success() {
193 | Ok(CompiledExercise {
194 | exercise: self,
195 | _handle: FileHandle,
196 | })
197 | } else {
198 | clean();
199 | Err(ExerciseOutput {
200 | stdout: String::from_utf8_lossy(&cmd.stdout).to_string(),
201 | stderr: String::from_utf8_lossy(&cmd.stderr).to_string(),
202 | })
203 | }
204 | }
205 |
206 | fn run(&self) -> Result {
207 | let arg = match self.mode {
208 | Mode::Test => "--show-output",
209 | Mode::BuildScript => return Ok(ExerciseOutput {
210 | stdout: "".to_string(),
211 | stderr: "".to_string(),
212 | }),
213 | _ => "",
214 | };
215 | println!("pa={}", temp_file());
216 | let cmd = Command::new(&temp_file())
217 | .arg(arg)
218 | .output()
219 | .expect("Failed to run 'run' command");
220 |
221 | let output = ExerciseOutput {
222 | stdout: String::from_utf8_lossy(&cmd.stdout).to_string(),
223 | stderr: String::from_utf8_lossy(&cmd.stderr).to_string(),
224 | };
225 |
226 | if cmd.status.success() {
227 | Ok(output)
228 | } else {
229 | Err(output)
230 | }
231 | }
232 |
233 | pub fn state(&self) -> State {
234 | let mut source_file =
235 | File::open(&self.path).expect("We were unable to open the exercise file!");
236 |
237 | let source = {
238 | let mut s = String::new();
239 | source_file
240 | .read_to_string(&mut s)
241 | .expect("We were unable to read the exercise file!");
242 | s
243 | };
244 |
245 | let re = Regex::new(I_AM_DONE_REGEX).unwrap();
246 |
247 | if !re.is_match(&source) {
248 | return State::Done;
249 | }
250 |
251 | let matched_line_index = source
252 | .lines()
253 | .enumerate()
254 | .find_map(|(i, line)| if re.is_match(line) { Some(i) } else { None })
255 | .expect("This should not happen at all");
256 |
257 | let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize;
258 | let max_line = matched_line_index + CONTEXT;
259 |
260 | let context = source
261 | .lines()
262 | .enumerate()
263 | .filter(|&(i, _)| i >= min_line && i <= max_line)
264 | .map(|(i, line)| ContextLine {
265 | line: line.to_string(),
266 | number: i + 1,
267 | important: i == matched_line_index,
268 | })
269 | .collect();
270 |
271 | State::Pending(context)
272 | }
273 |
274 | // Check that the exercise looks to be solved using self.state()
275 | // This is not the best way to check since
276 | // the user can just remove the "I AM NOT DONE" string from the file
277 | // without actually having solved anything.
278 | // The only other way to truly check this would to compile and run
279 | // the exercise; which would be both costly and counterintuitive
280 | pub fn looks_done(&self) -> bool {
281 | self.state() == State::Done
282 | }
283 | }
284 |
285 | impl Display for Exercise {
286 | fn fmt(&self, f: &mut Formatter) -> fmt::Result {
287 | write!(f, "{}", self.path.to_str().unwrap())
288 | }
289 | }
290 |
291 | #[inline]
292 | fn clean() {
293 | let _ignored = remove_file(&temp_file());
294 | }
295 |
296 | #[cfg(test)]
297 | mod test {
298 | use super::*;
299 | use std::path::Path;
300 |
301 | #[test]
302 | fn test_clean() {
303 | File::create(&temp_file()).unwrap();
304 | let exercise = Exercise {
305 | name: String::from("example"),
306 | path: PathBuf::from("tests/fixture/state/pending_exercise.rs"),
307 | mode: Mode::Compile,
308 | hint: String::from(""),
309 | };
310 | let compiled = exercise.compile().unwrap();
311 | drop(compiled);
312 | assert!(!Path::new(&temp_file()).exists());
313 | }
314 |
315 | #[test]
316 | fn test_pending_state() {
317 | let exercise = Exercise {
318 | name: "pending_exercise".into(),
319 | path: PathBuf::from("tests/fixture/state/pending_exercise.rs"),
320 | mode: Mode::Compile,
321 | hint: String::new(),
322 | };
323 |
324 | let state = exercise.state();
325 | let expected = vec![
326 | ContextLine {
327 | line: "// fake_exercise".to_string(),
328 | number: 1,
329 | important: false,
330 | },
331 | ContextLine {
332 | line: "".to_string(),
333 | number: 2,
334 | important: false,
335 | },
336 | ContextLine {
337 | line: "// I AM NOT DONE".to_string(),
338 | number: 3,
339 | important: true,
340 | },
341 | ContextLine {
342 | line: "".to_string(),
343 | number: 4,
344 | important: false,
345 | },
346 | ContextLine {
347 | line: "fn main() {".to_string(),
348 | number: 5,
349 | important: false,
350 | },
351 | ];
352 |
353 | assert_eq!(state, State::Pending(expected));
354 | }
355 |
356 | #[test]
357 | fn test_finished_exercise() {
358 | let exercise = Exercise {
359 | name: "finished_exercise".into(),
360 | path: PathBuf::from("tests/fixture/state/finished_exercise.rs"),
361 | mode: Mode::Compile,
362 | hint: String::new(),
363 | };
364 |
365 | assert_eq!(exercise.state(), State::Done);
366 | }
367 |
368 | #[test]
369 | fn test_exercise_with_output() {
370 | let exercise = Exercise {
371 | name: "exercise_with_output".into(),
372 | path: PathBuf::from("tests/fixture/success/testSuccess.rs"),
373 | mode: Mode::Test,
374 | hint: String::new(),
375 | };
376 | let out = exercise.compile().unwrap().run().unwrap();
377 | assert!(out.stdout.contains("THIS TEST TOO SHALL PASS"));
378 | }
379 | }
380 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | use crate::exercise::{Exercise, ExerciseList};
2 | use crate::project::RustAnalyzerProject;
3 | use crate::run::{reset, run};
4 | use crate::verify::verify;
5 | use argh::FromArgs;
6 | use console::Emoji;
7 | use notify::DebouncedEvent;
8 | use notify::{RecommendedWatcher, RecursiveMode, Watcher};
9 | use serde::{Deserialize, Serialize};
10 | use std::ffi::OsStr;
11 | use std::fs;
12 | use std::io::{self, prelude::*};
13 | use std::path::Path;
14 | use std::process::{Command, Stdio};
15 | use std::sync::atomic::{AtomicBool, Ordering};
16 | use std::sync::mpsc::{channel, RecvTimeoutError};
17 | use std::sync::{Arc, Mutex};
18 | use std::thread;
19 | use std::time::Duration;
20 | use std::time::{UNIX_EPOCH, SystemTime};
21 |
22 | #[macro_use]
23 | mod ui;
24 |
25 | mod exercise;
26 | mod project;
27 | mod run;
28 | mod verify;
29 |
30 | // In sync with crate version
31 | const VERSION: &str = "5.5.1";
32 |
33 | #[derive(FromArgs, PartialEq, Debug)]
34 | /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
35 | struct Args {
36 | /// show outputs from the test exercises
37 | #[argh(switch)]
38 | nocapture: bool,
39 | /// show the executable version
40 | #[argh(switch, short = 'v')]
41 | version: bool,
42 | #[argh(subcommand)]
43 | nested: Option,
44 | }
45 |
46 | #[derive(FromArgs, PartialEq, Debug)]
47 | #[argh(subcommand)]
48 | enum Subcommands {
49 | Verify(VerifyArgs),
50 | Watch(WatchArgs),
51 | Run(RunArgs),
52 | Reset(ResetArgs),
53 | Hint(HintArgs),
54 | List(ListArgs),
55 | Lsp(LspArgs),
56 | CicvVerify(CicvVerifyArgs)
57 | }
58 |
59 | #[derive(FromArgs, PartialEq, Debug)]
60 | #[argh(subcommand, name = "cicvverify", description = "cicvverify")]
61 | struct CicvVerifyArgs {}
62 |
63 | #[derive(FromArgs, PartialEq, Debug)]
64 | #[argh(subcommand, name = "verify")]
65 | /// Verifies all exercises according to the recommended order
66 | struct VerifyArgs {}
67 |
68 | #[derive(FromArgs, PartialEq, Debug)]
69 | #[argh(subcommand, name = "watch")]
70 | /// Reruns `verify` when files were edited
71 | struct WatchArgs {
72 | /// show hints on success
73 | #[argh(switch)]
74 | success_hints: bool,
75 | }
76 |
77 | #[derive(FromArgs, PartialEq, Debug)]
78 | #[argh(subcommand, name = "run")]
79 | /// Runs/Tests a single exercise
80 | struct RunArgs {
81 | #[argh(positional)]
82 | /// the name of the exercise
83 | name: String,
84 | }
85 |
86 | #[derive(FromArgs, PartialEq, Debug)]
87 | #[argh(subcommand, name = "reset")]
88 | /// Resets a single exercise using "git stash -- "
89 | struct ResetArgs {
90 | #[argh(positional)]
91 | /// the name of the exercise
92 | name: String,
93 | }
94 |
95 | #[derive(FromArgs, PartialEq, Debug)]
96 | #[argh(subcommand, name = "hint")]
97 | /// Returns a hint for the given exercise
98 | struct HintArgs {
99 | #[argh(positional)]
100 | /// the name of the exercise
101 | name: String,
102 | }
103 |
104 | #[derive(FromArgs, PartialEq, Debug)]
105 | #[argh(subcommand, name = "lsp")]
106 | /// Enable rust-analyzer for exercises
107 | struct LspArgs {}
108 |
109 | #[derive(FromArgs, PartialEq, Debug)]
110 | #[argh(subcommand, name = "list")]
111 | /// Lists the exercises available in Rustlings
112 | struct ListArgs {
113 | #[argh(switch, short = 'p')]
114 | /// show only the paths of the exercises
115 | paths: bool,
116 | #[argh(switch, short = 'n')]
117 | /// show only the names of the exercises
118 | names: bool,
119 | #[argh(option, short = 'f')]
120 | /// provide a string to match exercise names
121 | /// comma separated patterns are acceptable
122 | filter: Option,
123 | #[argh(switch, short = 'u')]
124 | /// display only exercises not yet solved
125 | unsolved: bool,
126 | #[argh(switch, short = 's')]
127 | /// display only exercises that have been solved
128 | solved: bool,
129 | }
130 |
131 | #[derive(Deserialize, Serialize)]
132 | pub struct ExerciseCheckList {
133 | pub exercises: Vec,
134 | pub user_name: Option,
135 | pub statistics: ExerciseStatistics
136 | }
137 |
138 | #[derive(Deserialize, Serialize)]
139 | pub struct ExerciseResult {
140 | pub name: String,
141 | pub result: bool
142 | }
143 |
144 | #[derive(Deserialize, Serialize)]
145 | pub struct ExerciseStatistics {
146 | pub total_exercations: usize,
147 | pub total_succeeds: usize,
148 | pub total_failures: usize,
149 | pub total_time: u32,
150 | }
151 |
152 | #[tokio::main]
153 | async fn main() {
154 | let args: Args = argh::from_env();
155 |
156 | if args.version {
157 | println!("v{VERSION}");
158 | std::process::exit(0);
159 | }
160 |
161 | if args.nested.is_none() {
162 | println!("\n{WELCOME}\n");
163 | }
164 |
165 | if !Path::new("info.toml").exists() {
166 | println!(
167 | "{} must be run from the rustlings directory",
168 | std::env::current_exe().unwrap().to_str().unwrap()
169 | );
170 | println!("Try `cd rustlings/`!");
171 | std::process::exit(1);
172 | }
173 |
174 | if !rustc_exists() {
175 | println!("We cannot find `rustc`.");
176 | println!("Try running `rustc --version` to diagnose your problem.");
177 | println!("For instructions on how to install Rust, check the README.");
178 | std::process::exit(1);
179 | }
180 |
181 | let toml_str = &fs::read_to_string("info.toml").unwrap();
182 | let exercises = toml::from_str::(toml_str).unwrap().exercises;
183 | let verbose = args.nocapture;
184 |
185 | let command = args.nested.unwrap_or_else(|| {
186 | println!("{DEFAULT_OUT}\n");
187 | std::process::exit(0);
188 | });
189 | match command {
190 | Subcommands::List(subargs) => {
191 | if !subargs.paths && !subargs.names {
192 | println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status");
193 | }
194 | let mut exercises_done: u16 = 0;
195 | let filters = subargs.filter.clone().unwrap_or_default().to_lowercase();
196 | exercises.iter().for_each(|e| {
197 | let fname = format!("{}", e.path.display());
198 | let filter_cond = filters
199 | .split(',')
200 | .filter(|f| !f.trim().is_empty())
201 | .any(|f| e.name.contains(&f) || fname.contains(&f));
202 | let status = if e.looks_done() {
203 | exercises_done += 1;
204 | "Done"
205 | } else {
206 | "Pending"
207 | };
208 | let solve_cond = {
209 | (e.looks_done() && subargs.solved)
210 | || (!e.looks_done() && subargs.unsolved)
211 | || (!subargs.solved && !subargs.unsolved)
212 | };
213 | if solve_cond && (filter_cond || subargs.filter.is_none()) {
214 | let line = if subargs.paths {
215 | format!("{fname}\n")
216 | } else if subargs.names {
217 | format!("{}\n", e.name)
218 | } else {
219 | format!("{:<17}\t{fname:<46}\t{status:<7}\n", e.name)
220 | };
221 | // Somehow using println! leads to the binary panicking
222 | // when its output is piped.
223 | // So, we're handling a Broken Pipe error and exiting with 0 anyway
224 | let stdout = std::io::stdout();
225 | {
226 | let mut handle = stdout.lock();
227 | handle.write_all(line.as_bytes()).unwrap_or_else(|e| {
228 | match e.kind() {
229 | std::io::ErrorKind::BrokenPipe => std::process::exit(0),
230 | _ => std::process::exit(1),
231 | };
232 | });
233 | }
234 | }
235 | });
236 | let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0;
237 | println!(
238 | "Progress: You completed {} / {} exercises ({:.1} %).",
239 | exercises_done,
240 | exercises.len(),
241 | percentage_progress
242 | );
243 | std::process::exit(0);
244 | }
245 |
246 | Subcommands::Run(subargs) => {
247 | let exercise = find_exercise(&subargs.name, &exercises);
248 | run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1));
249 | }
250 |
251 | Subcommands::Reset(subargs) => {
252 | let exercise = find_exercise(&subargs.name, &exercises);
253 |
254 | reset(exercise).unwrap_or_else(|_| std::process::exit(1));
255 | }
256 |
257 | Subcommands::Hint(subargs) => {
258 | let exercise = find_exercise(&subargs.name, &exercises);
259 |
260 | println!("{}", exercise.hint);
261 | }
262 |
263 | Subcommands::Verify(_subargs) => {
264 | verify(&exercises, (0, exercises.len()), verbose, false)
265 | .unwrap_or_else(|_| std::process::exit(1));
266 | }
267 |
268 | Subcommands::CicvVerify(_subargs) => {
269 | // let toml_str = &fs::read_to_string("info.toml").unwrap();
270 | // exercises = toml::from_str::(toml_str).unwrap().exercises;
271 | let now_start = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
272 | let rights = Arc::new(Mutex::new(0));
273 | let alls = exercises.len();
274 |
275 | let exercise_check_list = Arc::new(Mutex::new(
276 | ExerciseCheckList {
277 | exercises: vec![],
278 | user_name: None,
279 | statistics: ExerciseStatistics {
280 | total_exercations: alls,
281 | total_succeeds: 0,
282 | total_failures: 0,
283 | total_time: 0,
284 | }
285 | }
286 | ));
287 |
288 | let mut tasks = vec![];
289 | for exercise in exercises {
290 | let now_start = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
291 | let inner_exercise = exercise;
292 | let c_mutex = Arc::clone(&rights);
293 | let exercise_check_list_ref = Arc::clone(&exercise_check_list);
294 | let _verbose = verbose.clone();
295 | let t = tokio::task::spawn( async move {
296 | match run(&inner_exercise, true) {
297 | // match verify(vec![&inner_exercise], (0, 1), true, true) {
298 | Ok(_) => {
299 | *c_mutex.lock().unwrap() += 1;
300 | println!("{}执行成功", inner_exercise.name);
301 | println!("总的题目数: {}", alls);
302 | println!("当前做正确的题目数: {}", *c_mutex.lock().unwrap());
303 | let now_end = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
304 | println!("当前修改试卷耗时: {} s", now_end - now_start);
305 | exercise_check_list_ref.lock().unwrap().exercises.push(ExerciseResult{
306 | name: inner_exercise.name, result: true,
307 | });
308 | exercise_check_list_ref.lock().unwrap().statistics.total_succeeds += 1;
309 | },
310 | Err(_) => {
311 | println!("{}执行失败", inner_exercise.name);
312 | println!("总的题目数: {}", alls);
313 | println!("当前做正确的题目数: {}", *c_mutex.lock().unwrap());
314 | let now_end = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
315 | println!("当前修改试卷耗时: {} s", now_end - now_start);
316 | exercise_check_list_ref.lock().unwrap().exercises.push(ExerciseResult{
317 | name: inner_exercise.name, result: false,
318 | });
319 | exercise_check_list_ref.lock().unwrap().statistics.total_failures += 1;
320 | }
321 | }
322 | });
323 | tasks.push(t);
324 | }
325 | for task in tasks { task.await.unwrap(); }
326 | let now_end = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
327 | let total_time = now_end - now_start;
328 | println!("===============================试卷批改完成,总耗时: {} s; ==================================", total_time);
329 | let exercise_check_list_ref = Arc::clone(&exercise_check_list);
330 | exercise_check_list_ref.lock().unwrap().statistics.total_time = total_time as u32;
331 | let serialized = serde_json::to_string_pretty(&*exercise_check_list.lock().unwrap()).unwrap();
332 | fs::write(".github/result/check_result.json", serialized).unwrap();
333 | },
334 |
335 | Subcommands::Lsp(_subargs) => {
336 | let mut project = RustAnalyzerProject::new();
337 | project
338 | .get_sysroot_src()
339 | .expect("Couldn't find toolchain path, do you have `rustc` installed?");
340 | project
341 | .exercises_to_json()
342 | .expect("Couldn't parse rustlings exercises files");
343 |
344 | if project.crates.is_empty() {
345 | println!("Failed find any exercises, make sure you're in the `rustlings` folder");
346 | } else if project.write_to_disk().is_err() {
347 | println!("Failed to write rust-project.json to disk for rust-analyzer");
348 | } else {
349 | println!("Successfully generated rust-project.json");
350 | println!("rust-analyzer will now parse exercises, restart your language server or editor")
351 | }
352 | }
353 |
354 | Subcommands::Watch(_subargs) => match watch(&exercises, verbose, _subargs.success_hints) {
355 | Err(e) => {
356 | println!(
357 | "Error: Could not watch your progress. Error message was {:?}.",
358 | e
359 | );
360 | println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
361 | std::process::exit(1);
362 | }
363 | Ok(WatchStatus::Finished) => {
364 | println!(
365 | "{emoji} All exercises completed! {emoji}",
366 | emoji = Emoji("🎉", "★")
367 | );
368 | println!("\n{FENISH_LINE}\n");
369 | }
370 | Ok(WatchStatus::Unfinished) => {
371 | println!("We hope you're enjoying learning about Rust!");
372 | println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again");
373 | }
374 | },
375 | }
376 | }
377 |
378 | fn spawn_watch_shell(
379 | failed_exercise_hint: &Arc>>,
380 | should_quit: Arc,
381 | ) {
382 | let failed_exercise_hint = Arc::clone(failed_exercise_hint);
383 | println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.");
384 | thread::spawn(move || loop {
385 | let mut input = String::new();
386 | match io::stdin().read_line(&mut input) {
387 | Ok(_) => {
388 | let input = input.trim();
389 | if input == "hint" {
390 | if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
391 | println!("{hint}");
392 | }
393 | } else if input == "clear" {
394 | println!("\x1B[2J\x1B[1;1H");
395 | } else if input.eq("quit") {
396 | should_quit.store(true, Ordering::SeqCst);
397 | println!("Bye!");
398 | } else if input.eq("help") {
399 | println!("Commands available to you in watch mode:");
400 | println!(" hint - prints the current exercise's hint");
401 | println!(" clear - clears the screen");
402 | println!(" quit - quits watch mode");
403 | println!(" ! - executes a command, like `!rustc --explain E0381`");
404 | println!(" help - displays this help message");
405 | println!();
406 | println!("Watch mode automatically re-evaluates the current exercise");
407 | println!("when you edit a file's contents.")
408 | } else if let Some(cmd) = input.strip_prefix('!') {
409 | let parts: Vec<&str> = cmd.split_whitespace().collect();
410 | if parts.is_empty() {
411 | println!("no command provided");
412 | } else if let Err(e) = Command::new(parts[0]).args(&parts[1..]).status() {
413 | println!("failed to execute command `{}`: {}", cmd, e);
414 | }
415 | } else {
416 | println!("unknown command: {input}");
417 | }
418 | }
419 | Err(error) => println!("error reading command: {error}"),
420 | }
421 | });
422 | }
423 |
424 | fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
425 | if name.eq("next") {
426 | exercises
427 | .iter()
428 | .find(|e| !e.looks_done())
429 | .unwrap_or_else(|| {
430 | println!("🎉 Congratulations! You have done all the exercises!");
431 | println!("🔚 There are no more exercises to do next!");
432 | std::process::exit(1)
433 | })
434 | } else {
435 | exercises
436 | .iter()
437 | .find(|e| e.name == name)
438 | .unwrap_or_else(|| {
439 | println!("No exercise found for '{name}'!");
440 | std::process::exit(1)
441 | })
442 | }
443 | }
444 |
445 | enum WatchStatus {
446 | Finished,
447 | Unfinished,
448 | }
449 |
450 | fn watch(
451 | exercises: &[Exercise],
452 | verbose: bool,
453 | success_hints: bool,
454 | ) -> notify::Result {
455 | /* Clears the terminal with an ANSI escape code.
456 | Works in UNIX and newer Windows terminals. */
457 | fn clear_screen() {
458 | println!("\x1Bc");
459 | }
460 |
461 | let (tx, rx) = channel();
462 | let should_quit = Arc::new(AtomicBool::new(false));
463 |
464 | let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(1))?;
465 | watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;
466 |
467 | clear_screen();
468 |
469 | let to_owned_hint = |t: &Exercise| t.hint.to_owned();
470 | let failed_exercise_hint = match verify(
471 | exercises.iter(),
472 | (0, exercises.len()),
473 | verbose,
474 | success_hints,
475 | ) {
476 | Ok(_) => return Ok(WatchStatus::Finished),
477 | Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))),
478 | };
479 | spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit));
480 | loop {
481 | match rx.recv_timeout(Duration::from_secs(1)) {
482 | Ok(event) => match event {
483 | DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
484 | if b.extension() == Some(OsStr::new("rs")) && b.exists() {
485 | let filepath = b.as_path().canonicalize().unwrap();
486 | let pending_exercises = exercises
487 | .iter()
488 | .find(|e| filepath.ends_with(&e.path))
489 | .into_iter()
490 | .chain(
491 | exercises
492 | .iter()
493 | .filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)),
494 | );
495 | let num_done = exercises.iter().filter(|e| e.looks_done()).count();
496 | clear_screen();
497 | match verify(
498 | pending_exercises,
499 | (num_done, exercises.len()),
500 | verbose,
501 | success_hints,
502 | ) {
503 | Ok(_) => return Ok(WatchStatus::Finished),
504 | Err(exercise) => {
505 | let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
506 | *failed_exercise_hint = Some(to_owned_hint(exercise));
507 | }
508 | }
509 | }
510 | }
511 | _ => {}
512 | },
513 | Err(RecvTimeoutError::Timeout) => {
514 | // the timeout expired, just check the `should_quit` variable below then loop again
515 | }
516 | Err(e) => println!("watch error: {e:?}"),
517 | }
518 | // Check if we need to exit
519 | if should_quit.load(Ordering::SeqCst) {
520 | return Ok(WatchStatus::Unfinished);
521 | }
522 | }
523 | }
524 |
525 | fn rustc_exists() -> bool {
526 | Command::new("rustc")
527 | .args(&["--version"])
528 | .stdout(Stdio::null())
529 | .spawn()
530 | .and_then(|mut child| child.wait())
531 | .map(|status| status.success())
532 | .unwrap_or(false)
533 | }
534 |
535 | const DEFAULT_OUT: &str = r#"Thanks for installing Rustlings!
536 |
537 | Is this your first time? Don't worry, Rustlings was made for beginners! We are
538 | going to teach you a lot of things about Rust, but before we can get
539 | started, here's a couple of notes about how Rustlings operates:
540 |
541 | 1. The central concept behind Rustlings is that you solve exercises. These
542 | exercises usually have some sort of syntax error in them, which will cause
543 | them to fail compilation or testing. Sometimes there's a logic error instead
544 | of a syntax error. No matter what error, it's your job to find it and fix it!
545 | You'll know when you fixed it because then, the exercise will compile and
546 | Rustlings will be able to move on to the next exercise.
547 | 2. If you run Rustlings in watch mode (which we recommend), it'll automatically
548 | start with the first exercise. Don't get confused by an error message popping
549 | up as soon as you run Rustlings! This is part of the exercise that you're
550 | supposed to solve, so open the exercise file in an editor and start your
551 | detective work!
552 | 3. If you're stuck on an exercise, there is a helpful hint you can view by typing
553 | 'hint' (in watch mode), or running `rustlings hint exercise_name`.
554 | 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub!
555 | (https://github.com/rust-lang/rustlings/issues/new). We look at every issue,
556 | and sometimes, other learners do too so you can help each other out!
557 | 5. If you want to use `rust-analyzer` with exercises, which provides features like
558 | autocompletion, run the command `rustlings lsp`.
559 |
560 | Got all that? Great! To get started, run `rustlings watch` in order to get the first
561 | exercise. Make sure to have your editor open!"#;
562 |
563 | const FENISH_LINE: &str = r#"+----------------------------------------------------+
564 | | You made it to the Fe-nish line! |
565 | +-------------------------- ------------------------+
566 | \\/
567 | ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒
568 | ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒
569 | ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒
570 | ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒
571 | ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓
572 | ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒
573 | ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒
574 | ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒
575 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
576 | ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒
577 | ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒
578 | ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒
579 | ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒
580 | ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒
581 | ▒▒ ▒▒ ▒▒ ▒▒
582 |
583 | We hope you enjoyed learning about the various aspects of Rust!
584 | If you noticed any issues, please don't hesitate to report them to our repo.
585 | You can also contribute your own exercises to help the greater community!
586 |
587 | Before reporting an issue or contributing, please read our guidelines:
588 | https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"#;
589 |
590 | const WELCOME: &str = r#" welcome to...
591 | _ _ _
592 | _ __ _ _ ___| |_| (_)_ __ __ _ ___
593 | | '__| | | / __| __| | | '_ \ / _` / __|
594 | | | | |_| \__ \ |_| | | | | | (_| \__ \
595 | |_| \__,_|___/\__|_|_|_| |_|\__, |___/
596 | |___/"#;
597 |
--------------------------------------------------------------------------------
/src/project.rs:
--------------------------------------------------------------------------------
1 | use glob::glob;
2 | use serde::{Deserialize, Serialize};
3 | use std::env;
4 | use std::error::Error;
5 | use std::path::PathBuf;
6 | use std::process::Command;
7 |
8 | /// Contains the structure of resulting rust-project.json file
9 | /// and functions to build the data required to create the file
10 | #[derive(Serialize, Deserialize)]
11 | pub struct RustAnalyzerProject {
12 | sysroot_src: String,
13 | pub crates: Vec,
14 | }
15 |
16 | #[derive(Serialize, Deserialize)]
17 | pub struct Crate {
18 | root_module: String,
19 | edition: String,
20 | deps: Vec,
21 | cfg: Vec,
22 | }
23 |
24 | impl RustAnalyzerProject {
25 | pub fn new() -> RustAnalyzerProject {
26 | RustAnalyzerProject {
27 | sysroot_src: String::new(),
28 | crates: Vec::new(),
29 | }
30 | }
31 |
32 | /// Write rust-project.json to disk
33 | pub fn write_to_disk(&self) -> Result<(), std::io::Error> {
34 | std::fs::write(
35 | "./rust-project.json",
36 | serde_json::to_vec(&self).expect("Failed to serialize to JSON"),
37 | )?;
38 | Ok(())
39 | }
40 |
41 | /// If path contains .rs extension, add a crate to `rust-project.json`
42 | fn path_to_json(&mut self, path: PathBuf) -> Result<(), Box> {
43 | if let Some(ext) = path.extension() {
44 | if ext == "rs" {
45 | self.crates.push(Crate {
46 | root_module: path.display().to_string(),
47 | edition: "2021".to_string(),
48 | deps: Vec::new(),
49 | // This allows rust_analyzer to work inside #[test] blocks
50 | cfg: vec!["test".to_string()],
51 | })
52 | }
53 | }
54 |
55 | Ok(())
56 | }
57 |
58 | /// Parse the exercises folder for .rs files, any matches will create
59 | /// a new `crate` in rust-project.json which allows rust-analyzer to
60 | /// treat it like a normal binary
61 | pub fn exercises_to_json(&mut self) -> Result<(), Box> {
62 | for path in glob("./exercises/**/*")? {
63 | self.path_to_json(path?)?;
64 | }
65 | Ok(())
66 | }
67 |
68 | /// Use `rustc` to determine the default toolchain
69 | pub fn get_sysroot_src(&mut self) -> Result<(), Box> {
70 | // check if RUST_SRC_PATH is set
71 | if let Ok(path) = env::var("RUST_SRC_PATH") {
72 | self.sysroot_src = path;
73 | return Ok(());
74 | }
75 |
76 | let toolchain = Command::new("rustc")
77 | .arg("--print")
78 | .arg("sysroot")
79 | .output()?
80 | .stdout;
81 |
82 | let toolchain = String::from_utf8_lossy(&toolchain);
83 | let mut whitespace_iter = toolchain.split_whitespace();
84 |
85 | let toolchain = whitespace_iter.next().unwrap_or(&toolchain);
86 |
87 | println!("Determined toolchain: {}\n", &toolchain);
88 |
89 | self.sysroot_src = (std::path::Path::new(&*toolchain)
90 | .join("lib")
91 | .join("rustlib")
92 | .join("src")
93 | .join("rust")
94 | .join("library")
95 | .to_string_lossy())
96 | .to_string();
97 | Ok(())
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/run.rs:
--------------------------------------------------------------------------------
1 | use std::process::Command;
2 |
3 | use crate::exercise::{Exercise, Mode};
4 | use crate::verify::test;
5 | use indicatif::ProgressBar;
6 |
7 | // Invoke the rust compiler on the path of the given exercise,
8 | // and run the ensuing binary.
9 | // The verbose argument helps determine whether or not to show
10 | // the output from the test harnesses (if the mode of the exercise is test)
11 | pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
12 | match exercise.mode {
13 | Mode::Test => test(exercise, verbose)?,
14 | Mode::Compile => compile_and_run(exercise)?,
15 | Mode::Clippy => compile_and_run(exercise)?,
16 | Mode::BuildScript => test(exercise, verbose)?,
17 | }
18 | Ok(())
19 | }
20 |
21 | // Resets the exercise by stashing the changes.
22 | pub fn reset(exercise: &Exercise) -> Result<(), ()> {
23 | let command = Command::new("git")
24 | .args(["stash", "--"])
25 | .arg(&exercise.path)
26 | .spawn();
27 |
28 | match command {
29 | Ok(_) => Ok(()),
30 | Err(_) => Err(()),
31 | }
32 | }
33 |
34 | // Invoke the rust compiler on the path of the given exercise
35 | // and run the ensuing binary.
36 | // This is strictly for non-test binaries, so output is displayed
37 | fn compile_and_run(exercise: &Exercise) -> Result<(), ()> {
38 | let progress_bar = ProgressBar::new_spinner();
39 | progress_bar.set_message(format!("Compiling {exercise}..."));
40 | progress_bar.enable_steady_tick(100);
41 |
42 | let compilation_result = exercise.compile();
43 | let compilation = match compilation_result {
44 | Ok(compilation) => compilation,
45 | Err(output) => {
46 | progress_bar.finish_and_clear();
47 | warn!(
48 | "Compilation of {} failed!, Compiler error message:\n",
49 | exercise
50 | );
51 | println!("{}", output.stderr);
52 | return Err(());
53 | }
54 | };
55 |
56 | progress_bar.set_message(format!("Running {exercise}..."));
57 | let result = compilation.run();
58 | progress_bar.finish_and_clear();
59 |
60 | match result {
61 | Ok(output) => {
62 | println!("{}", output.stdout);
63 | success!("Successfully ran {}", exercise);
64 | Ok(())
65 | }
66 | Err(output) => {
67 | println!("{}", output.stdout);
68 | println!("{}", output.stderr);
69 |
70 | warn!("Ran {} with errors", exercise);
71 | Err(())
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/ui.rs:
--------------------------------------------------------------------------------
1 | macro_rules! warn {
2 | ($fmt:literal, $ex:expr) => {{
3 | use console::{style, Emoji};
4 | use std::env;
5 | let formatstr = format!($fmt, $ex);
6 | if env::var("NO_EMOJI").is_ok() {
7 | println!("{} {}", style("!").red(), style(formatstr).red());
8 | } else {
9 | println!(
10 | "{} {}",
11 | style(Emoji("⚠️ ", "!")).red(),
12 | style(formatstr).red()
13 | );
14 | }
15 | }};
16 | }
17 |
18 | macro_rules! success {
19 | ($fmt:literal, $ex:expr) => {{
20 | use console::{style, Emoji};
21 | use std::env;
22 | let formatstr = format!($fmt, $ex);
23 | if env::var("NO_EMOJI").is_ok() {
24 | println!("{} {}", style("✓").green(), style(formatstr).green());
25 | } else {
26 | println!(
27 | "{} {}",
28 | style(Emoji("✅", "✓")).green(),
29 | style(formatstr).green()
30 | );
31 | }
32 | }};
33 | }
34 |
--------------------------------------------------------------------------------
/src/verify.rs:
--------------------------------------------------------------------------------
1 | use crate::exercise::{CompiledExercise, Exercise, Mode, State};
2 | use console::style;
3 | use indicatif::{ProgressBar, ProgressStyle};
4 | use std::env;
5 |
6 | // Verify that the provided container of Exercise objects
7 | // can be compiled and run without any failures.
8 | // Any such failures will be reported to the end user.
9 | // If the Exercise being verified is a test, the verbose boolean
10 | // determines whether or not the test harness outputs are displayed.
11 | pub fn verify<'a>(
12 | exercises: impl IntoIterator- ,
13 | progress: (usize, usize),
14 | verbose: bool,
15 | success_hints: bool,
16 | ) -> Result<(), &'a Exercise> {
17 | let (num_done, total) = progress;
18 | let bar = ProgressBar::new(total as u64);
19 | let mut percentage = num_done as f32 / total as f32 * 100.0;
20 | bar.set_style(ProgressStyle::default_bar()
21 | .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}")
22 | .progress_chars("#>-")
23 | );
24 | bar.set_position(num_done as u64);
25 | bar.set_message(format!("({:.1} %)", percentage));
26 |
27 | for exercise in exercises {
28 | let compile_result = match exercise.mode {
29 | Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints),
30 | Mode::Compile => compile_and_run_interactively(exercise, success_hints),
31 | Mode::Clippy => compile_only(exercise, success_hints),
32 | Mode::BuildScript => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints),
33 |
34 | };
35 | if !compile_result.unwrap_or(false) {
36 | return Err(exercise);
37 | }
38 | percentage += 100.0 / total as f32;
39 | bar.inc(1);
40 | bar.set_message(format!("({:.1} %)", percentage));
41 | }
42 | Ok(())
43 | }
44 |
45 | enum RunMode {
46 | Interactive,
47 | NonInteractive,
48 | }
49 |
50 | // Compile and run the resulting test harness of the given Exercise
51 | pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
52 | compile_and_test(exercise, RunMode::NonInteractive, verbose, false)?;
53 | Ok(())
54 | }
55 |
56 | // Invoke the rust compiler without running the resulting binary
57 | fn compile_only(exercise: &Exercise, success_hints: bool) -> Result {
58 | let progress_bar = ProgressBar::new_spinner();
59 | progress_bar.set_message(format!("Compiling {exercise}..."));
60 | progress_bar.enable_steady_tick(100);
61 |
62 | let _ = compile(exercise, &progress_bar)?;
63 | progress_bar.finish_and_clear();
64 |
65 | Ok(prompt_for_completion(exercise, None, success_hints))
66 | }
67 |
68 | // Compile the given Exercise and run the resulting binary in an interactive mode
69 | fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result {
70 | let progress_bar = ProgressBar::new_spinner();
71 | progress_bar.set_message(format!("Compiling {exercise}..."));
72 | progress_bar.enable_steady_tick(100);
73 |
74 | let compilation = compile(exercise, &progress_bar)?;
75 |
76 | progress_bar.set_message(format!("Running {exercise}..."));
77 | let result = compilation.run();
78 | progress_bar.finish_and_clear();
79 |
80 | let output = match result {
81 | Ok(output) => output,
82 | Err(output) => {
83 | warn!("Ran {} with errors", exercise);
84 | println!("{}", output.stdout);
85 | println!("{}", output.stderr);
86 | return Err(());
87 | }
88 | };
89 |
90 | Ok(prompt_for_completion(exercise, Some(output.stdout), success_hints))
91 | }
92 |
93 | // Compile the given Exercise as a test harness and display
94 | // the output if verbose is set to true
95 | fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool, success_hints: bool) -> Result {
96 | let progress_bar = ProgressBar::new_spinner();
97 | progress_bar.set_message(format!("Testing {exercise}..."));
98 | progress_bar.enable_steady_tick(100);
99 |
100 | let compilation = compile(exercise, &progress_bar)?;
101 | let result = compilation.run();
102 | progress_bar.finish_and_clear();
103 |
104 | match result {
105 | Ok(output) => {
106 | if verbose {
107 | println!("{}", output.stdout);
108 | }
109 | if let RunMode::Interactive = run_mode {
110 | Ok(prompt_for_completion(exercise, None, success_hints))
111 | } else {
112 | Ok(true)
113 | }
114 | }
115 | Err(output) => {
116 | warn!(
117 | "Testing of {} failed! Please try again. Here's the output:",
118 | exercise
119 | );
120 | println!("{}", output.stdout);
121 | Err(())
122 | }
123 | }
124 | }
125 |
126 | // Compile the given Exercise and return an object with information
127 | // about the state of the compilation
128 | fn compile<'a, 'b>(
129 | exercise: &'a Exercise,
130 | progress_bar: &'b ProgressBar,
131 | ) -> Result, ()> {
132 | let compilation_result = exercise.compile();
133 |
134 | match compilation_result {
135 | Ok(compilation) => Ok(compilation),
136 | Err(output) => {
137 | progress_bar.finish_and_clear();
138 | warn!(
139 | "Compiling of {} failed! Please try again. Here's the output:",
140 | exercise
141 | );
142 | println!("{}", output.stderr);
143 | Err(())
144 | }
145 | }
146 | }
147 |
148 | fn prompt_for_completion(exercise: &Exercise, prompt_output: Option, success_hints: bool) -> bool {
149 | let context = match exercise.state() {
150 | State::Done => return true,
151 | State::Pending(context) => context,
152 | };
153 | match exercise.mode {
154 | Mode::Compile => success!("Successfully ran {}!", exercise),
155 | Mode::Test => success!("Successfully tested {}!", exercise),
156 | Mode::Clippy => success!("Successfully compiled {}!", exercise),
157 | Mode::BuildScript => success!("Successfully compiled {}!", exercise),
158 | }
159 |
160 | let no_emoji = env::var("NO_EMOJI").is_ok();
161 |
162 | let clippy_success_msg = if no_emoji {
163 | "The code is compiling, and Clippy is happy!"
164 | } else {
165 | "The code is compiling, and 📎 Clippy 📎 is happy!"
166 | };
167 |
168 | let success_msg = match exercise.mode {
169 | Mode::Compile => "The code is compiling!",
170 | Mode::Test => "The code is compiling, and the tests pass!",
171 | Mode::Clippy => clippy_success_msg,
172 | Mode::BuildScript => "Build script works!",
173 | };
174 | println!();
175 | if no_emoji {
176 | println!("~*~ {success_msg} ~*~")
177 | } else {
178 | println!("🎉 🎉 {success_msg} 🎉 🎉")
179 | }
180 | println!();
181 |
182 | if let Some(output) = prompt_output {
183 | println!("Output:");
184 | println!("{}", separator());
185 | println!("{output}");
186 | println!("{}", separator());
187 | println!();
188 | }
189 | if success_hints {
190 | println!("Hints:");
191 | println!("{}", separator());
192 | println!("{}", exercise.hint);
193 | println!("{}", separator());
194 | println!();
195 | }
196 |
197 | println!("You can keep working on this exercise,");
198 | println!(
199 | "or jump into the next one by removing the {} comment:",
200 | style("`I AM NOT DONE`").bold()
201 | );
202 | println!();
203 | for context_line in context {
204 | let formatted_line = if context_line.important {
205 | format!("{}", style(context_line.line).bold())
206 | } else {
207 | context_line.line.to_string()
208 | };
209 |
210 | println!(
211 | "{:>2} {} {}",
212 | style(context_line.number).blue().bold(),
213 | style("|").blue(),
214 | formatted_line
215 | );
216 | }
217 |
218 | false
219 | }
220 |
221 | fn separator() -> console::StyledObject<&'static str> {
222 | style("====================").bold()
223 | }
224 |
--------------------------------------------------------------------------------
/tests/cicv.rs:
--------------------------------------------------------------------------------
1 | use assert_cmd::prelude::*;
2 | use std::process::Command;
3 |
4 | #[test]
5 | fn cicvverify() {
6 | Command::cargo_bin("rustlings")
7 | .unwrap()
8 | .args(&["--nocapture", "cicvverify"])
9 | // .current_dir("exercises")
10 | .assert()
11 | .success();
12 | }
13 |
--------------------------------------------------------------------------------
/tests/fixture/failure/compFailure.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | let
3 | }
--------------------------------------------------------------------------------
/tests/fixture/failure/compNoExercise.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | }
3 |
--------------------------------------------------------------------------------
/tests/fixture/failure/info.toml:
--------------------------------------------------------------------------------
1 | [[exercises]]
2 | name = "compFailure"
3 | path = "compFailure.rs"
4 | mode = "compile"
5 | hint = ""
6 |
7 | [[exercises]]
8 | name = "testFailure"
9 | path = "testFailure.rs"
10 | mode = "test"
11 | hint = "Hello!"
12 |
--------------------------------------------------------------------------------
/tests/fixture/failure/testFailure.rs:
--------------------------------------------------------------------------------
1 | #[test]
2 | fn passing() {
3 | asset!(true);
4 | }
5 |
--------------------------------------------------------------------------------
/tests/fixture/failure/testNotPassed.rs:
--------------------------------------------------------------------------------
1 | #[test]
2 | fn not_passing() {
3 | assert!(false);
4 | }
5 |
--------------------------------------------------------------------------------
/tests/fixture/state/finished_exercise.rs:
--------------------------------------------------------------------------------
1 | // fake_exercise
2 |
3 | fn main() {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/tests/fixture/state/info.toml:
--------------------------------------------------------------------------------
1 | [[exercises]]
2 | name = "pending_exercise"
3 | path = "pending_exercise.rs"
4 | mode = "compile"
5 | hint = """"""
6 |
7 | [[exercises]]
8 | name = "pending_test_exercise"
9 | path = "pending_test_exercise.rs"
10 | mode = "test"
11 | hint = """"""
12 |
13 | [[exercises]]
14 | name = "finished_exercise"
15 | path = "finished_exercise.rs"
16 | mode = "compile"
17 | hint = """"""
18 |
19 |
--------------------------------------------------------------------------------
/tests/fixture/state/pending_exercise.rs:
--------------------------------------------------------------------------------
1 | // fake_exercise
2 |
3 | // I AM NOT DONE
4 |
5 | fn main() {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/tests/fixture/state/pending_test_exercise.rs:
--------------------------------------------------------------------------------
1 | // I AM NOT DONE
2 |
3 | #[test]
4 | fn it_works() {}
5 |
--------------------------------------------------------------------------------
/tests/fixture/success/compSuccess.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | }
3 |
--------------------------------------------------------------------------------
/tests/fixture/success/info.toml:
--------------------------------------------------------------------------------
1 | [[exercises]]
2 | name = "compSuccess"
3 | path = "compSuccess.rs"
4 | mode = "compile"
5 | hint = """"""
6 |
7 | [[exercises]]
8 | name = "testSuccess"
9 | path = "testSuccess.rs"
10 | mode = "test"
11 | hint = """"""
12 |
--------------------------------------------------------------------------------
/tests/fixture/success/testSuccess.rs:
--------------------------------------------------------------------------------
1 | #[test]
2 | fn passing() {
3 | println!("THIS TEST TOO SHALL PASS");
4 | assert!(true);
5 | }
6 |
--------------------------------------------------------------------------------
/tests/integration_tests.rs:
--------------------------------------------------------------------------------
1 | use assert_cmd::prelude::*;
2 | use glob::glob;
3 | use predicates::boolean::PredicateBooleanExt;
4 | use std::fs::File;
5 | use std::io::Read;
6 | use std::process::Command;
7 |
8 | #[test]
9 | fn runs_without_arguments() {
10 | let mut cmd = Command::cargo_bin("rustlings").unwrap();
11 | cmd.assert().success();
12 | }
13 |
14 | #[test]
15 | fn fails_when_in_wrong_dir() {
16 | Command::cargo_bin("rustlings")
17 | .unwrap()
18 | .current_dir("tests/")
19 | .assert()
20 | .code(1);
21 | }
22 |
23 | #[test]
24 | fn verify_all_success() {
25 | Command::cargo_bin("rustlings")
26 | .unwrap()
27 | .arg("verify")
28 | .current_dir("tests/fixture/success")
29 | .assert()
30 | .success();
31 | }
32 |
33 | #[test]
34 | fn verify_fails_if_some_fails() {
35 | Command::cargo_bin("rustlings")
36 | .unwrap()
37 | .arg("verify")
38 | .current_dir("tests/fixture/failure")
39 | .assert()
40 | .code(1);
41 | }
42 |
43 | #[test]
44 | fn run_single_compile_success() {
45 | Command::cargo_bin("rustlings")
46 | .unwrap()
47 | .args(&["run", "compSuccess"])
48 | .current_dir("tests/fixture/success/")
49 | .assert()
50 | .success();
51 | }
52 |
53 | #[test]
54 | fn run_single_compile_failure() {
55 | Command::cargo_bin("rustlings")
56 | .unwrap()
57 | .args(&["run", "compFailure"])
58 | .current_dir("tests/fixture/failure/")
59 | .assert()
60 | .code(1);
61 | }
62 |
63 | #[test]
64 | fn run_single_test_success() {
65 | Command::cargo_bin("rustlings")
66 | .unwrap()
67 | .args(&["run", "testSuccess"])
68 | .current_dir("tests/fixture/success/")
69 | .assert()
70 | .success();
71 | }
72 |
73 | #[test]
74 | fn run_single_test_failure() {
75 | Command::cargo_bin("rustlings")
76 | .unwrap()
77 | .args(&["run", "testFailure"])
78 | .current_dir("tests/fixture/failure/")
79 | .assert()
80 | .code(1);
81 | }
82 |
83 | #[test]
84 | fn run_single_test_not_passed() {
85 | Command::cargo_bin("rustlings")
86 | .unwrap()
87 | .args(&["run", "testNotPassed.rs"])
88 | .current_dir("tests/fixture/failure/")
89 | .assert()
90 | .code(1);
91 | }
92 |
93 | #[test]
94 | fn run_single_test_no_filename() {
95 | Command::cargo_bin("rustlings")
96 | .unwrap()
97 | .arg("run")
98 | .current_dir("tests/fixture/")
99 | .assert()
100 | .code(1);
101 | }
102 |
103 | #[test]
104 | fn run_single_test_no_exercise() {
105 | Command::cargo_bin("rustlings")
106 | .unwrap()
107 | .args(&["run", "compNoExercise.rs"])
108 | .current_dir("tests/fixture/failure")
109 | .assert()
110 | .code(1);
111 | }
112 |
113 | #[test]
114 | fn reset_single_exercise() {
115 | Command::cargo_bin("rustlings")
116 | .unwrap()
117 | .args(&["reset", "intro1"])
118 | .assert()
119 | .code(0);
120 | }
121 |
122 | #[test]
123 | fn reset_no_exercise() {
124 | Command::cargo_bin("rustlings")
125 | .unwrap()
126 | .arg("reset")
127 | .assert()
128 | .code(1)
129 | .stderr(predicates::str::contains(
130 | "positional arguments not provided",
131 | ));
132 | }
133 |
134 | #[test]
135 | fn get_hint_for_single_test() {
136 | Command::cargo_bin("rustlings")
137 | .unwrap()
138 | .args(&["hint", "testFailure"])
139 | .current_dir("tests/fixture/failure")
140 | .assert()
141 | .code(0)
142 | .stdout("Hello!\n");
143 | }
144 |
145 | #[test]
146 | fn all_exercises_require_confirmation() {
147 | for exercise in glob("exercises/**/*.rs").unwrap() {
148 | let path = exercise.unwrap();
149 | if path.file_name().unwrap() == "mod.rs" {
150 | continue;
151 | }
152 | let source = {
153 | let mut file = File::open(&path).unwrap();
154 | let mut s = String::new();
155 | file.read_to_string(&mut s).unwrap();
156 | s
157 | };
158 | source
159 | .matches("// I AM NOT DONE")
160 | .next()
161 | .unwrap_or_else(|| {
162 | panic!(
163 | "There should be an `I AM NOT DONE` annotation in {:?}",
164 | path
165 | )
166 | });
167 | }
168 | }
169 |
170 | #[test]
171 | fn run_compile_exercise_does_not_prompt() {
172 | Command::cargo_bin("rustlings")
173 | .unwrap()
174 | .args(&["run", "pending_exercise"])
175 | .current_dir("tests/fixture/state")
176 | .assert()
177 | .code(0)
178 | .stdout(predicates::str::contains("I AM NOT DONE").not());
179 | }
180 |
181 | #[test]
182 | fn run_test_exercise_does_not_prompt() {
183 | Command::cargo_bin("rustlings")
184 | .unwrap()
185 | .args(&["run", "pending_test_exercise"])
186 | .current_dir("tests/fixture/state")
187 | .assert()
188 | .code(0)
189 | .stdout(predicates::str::contains("I AM NOT DONE").not());
190 | }
191 |
192 | #[test]
193 | fn run_single_test_success_with_output() {
194 | Command::cargo_bin("rustlings")
195 | .unwrap()
196 | .args(&["--nocapture", "run", "testSuccess"])
197 | .current_dir("tests/fixture/success/")
198 | .assert()
199 | .code(0)
200 | .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS"));
201 | }
202 |
203 | #[test]
204 | fn run_single_test_success_without_output() {
205 | Command::cargo_bin("rustlings")
206 | .unwrap()
207 | .args(&["run", "testSuccess"])
208 | .current_dir("tests/fixture/success/")
209 | .assert()
210 | .code(0)
211 | .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS").not());
212 | }
213 |
214 | #[test]
215 | fn run_rustlings_list() {
216 | Command::cargo_bin("rustlings")
217 | .unwrap()
218 | .args(&["list"])
219 | .current_dir("tests/fixture/success")
220 | .assert()
221 | .success();
222 | }
223 |
224 | #[test]
225 | fn run_rustlings_list_no_pending() {
226 | Command::cargo_bin("rustlings")
227 | .unwrap()
228 | .args(&["list"])
229 | .current_dir("tests/fixture/success")
230 | .assert()
231 | .success()
232 | .stdout(predicates::str::contains("Pending").not());
233 | }
234 |
235 | #[test]
236 | fn run_rustlings_list_both_done_and_pending() {
237 | Command::cargo_bin("rustlings")
238 | .unwrap()
239 | .args(&["list"])
240 | .current_dir("tests/fixture/state")
241 | .assert()
242 | .success()
243 | .stdout(predicates::str::contains("Done").and(predicates::str::contains("Pending")));
244 | }
245 |
246 | #[test]
247 | fn run_rustlings_list_without_pending() {
248 | Command::cargo_bin("rustlings")
249 | .unwrap()
250 | .args(&["list", "--solved"])
251 | .current_dir("tests/fixture/state")
252 | .assert()
253 | .success()
254 | .stdout(predicates::str::contains("Pending").not());
255 | }
256 |
257 | #[test]
258 | fn run_rustlings_list_without_done() {
259 | Command::cargo_bin("rustlings")
260 | .unwrap()
261 | .args(&["list", "--unsolved"])
262 | .current_dir("tests/fixture/state")
263 | .assert()
264 | .success()
265 | .stdout(predicates::str::contains("Done").not());
266 | }
267 |
--------------------------------------------------------------------------------