mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-06-06 23:36:37 +02:00
Compare commits
413 Commits
rustlings-
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
cb60c8887c | ||
|
46814d397a | ||
|
734fc482eb | ||
|
520dfdc464 | ||
|
2267f99684 | ||
|
bf74a3d0a7 | ||
|
adf3ddd968 | ||
|
f80c2edc3d | ||
|
04520ae7ad | ||
|
e36dd7a120 | ||
|
edc8528dde | ||
|
47e490a997 | ||
|
596e7f36cc | ||
|
512ded81c4 | ||
|
69a9e9cafc | ||
|
54a74fd638 | ||
|
a51d6f1309 | ||
|
f6a657a0c3 | ||
|
8c24763259 | ||
|
dc468882cc | ||
|
5fc787f4e4 | ||
|
8fa598ae7e | ||
|
2f700991f3 | ||
|
b4a6b87e24 | ||
|
984e9fea7c | ||
|
8339007633 | ||
|
23b9aa3a15 | ||
|
69fe9626da | ||
|
f387f4c1d9 | ||
|
40fe3aa741 | ||
|
b30973afa1 | ||
|
3d8bef4bc3 | ||
|
2673177b17 | ||
|
6d5369d4d0 | ||
|
b9d1e636a4 | ||
|
7e26418952 | ||
|
61c17cb349 | ||
|
fda18e8895 | ||
|
7ec6986965 | ||
|
74ab9924b4 | ||
|
a28000acc4 | ||
|
08548abcc2 | ||
|
5927a781a3 | ||
|
e73fff3bd4 | ||
|
8dff0df266 | ||
|
5ee7dfb5c2 | ||
|
9a3586878d | ||
|
a99433c62d | ||
|
e76ca5e2b9 | ||
|
48bab77609 | ||
|
a063bcfb4c | ||
|
c5f49cfa48 | ||
|
9bcd4198c5 | ||
|
29dc8ea9fa | ||
|
fa91814aa9 | ||
|
0b91db2195 | ||
|
7b2d42b0f0 | ||
|
bd3bdd620b | ||
|
8b4562e102 | ||
|
63d8986f2a | ||
|
ecaecc2f76 | ||
|
78194b4441 | ||
|
44699e9b1b | ||
|
9978c17d5f | ||
|
3cc7e0377c | ||
|
d2abc359cc | ||
|
7c0d269279 | ||
|
8db85946af | ||
|
7019f4d178 | ||
|
fcd77a83cc | ||
|
ae444eb3da | ||
|
425c9821e0 | ||
|
46c6fb2c82 | ||
|
374c3874af | ||
|
1eb6c1e469 | ||
|
06af3ffc99 | ||
|
65dc019fa6 | ||
|
a56ccb6f4f | ||
|
d9872f2615 | ||
|
298be671b9 | ||
|
fbfd4f25e7 | ||
|
d12735a573 | ||
|
1aec7c1152 | ||
|
0b55809bb9 | ||
|
bde6f7470c | ||
|
53ec59ed95 | ||
|
ed1ee38923 | ||
|
26cf4989a2 | ||
|
6e60f441e9 | ||
|
d07de879a7 | ||
|
dd0634c483 | ||
|
fc0cd8f0f8 | ||
|
d5cae8ff59 | ||
|
38016cb2d6 | ||
|
e6cb104294 | ||
|
410eb69d25 | ||
|
243cf5f261 | ||
|
eff2ce8a23 | ||
|
fd33c29b26 | ||
|
f49164e69b | ||
|
9bc7bbe4b4 | ||
|
46ad25f925 | ||
|
2a725fb137 | ||
|
449858655d | ||
|
e8c2a79516 | ||
|
ea85c1b46e | ||
|
6bec6f92c4 | ||
|
930a0ea73b | ||
|
7e2f56f41a | ||
|
e90f5f03f3 | ||
|
0e090ae112 | ||
|
99496706c5 | ||
|
f146553dea | ||
|
0432e07864 | ||
|
f33ba139b4 | ||
|
990a722852 | ||
|
a675cb5754 | ||
|
baeeff389c | ||
|
932bc25d88 | ||
|
bdc6dad8de | ||
|
ea73af9ba3 | ||
|
fc5fc0920f | ||
|
9705c161b4 | ||
|
8cac21511c | ||
|
396ee4d618 | ||
|
326169a7fa | ||
|
685e069c58 | ||
|
84a42a2b24 | ||
|
ac6e1b7ce5 | ||
|
f516da4138 | ||
|
e852e60416 | ||
|
bf7d171915 | ||
|
d3f819f86f | ||
|
aa83fd6bc4 | ||
|
e2f7734f37 | ||
|
5c17abd1bf | ||
|
c52867eb8b | ||
|
26fd97a209 | ||
|
f0a2cdeb18 | ||
|
0c79f2ea3e | ||
|
0e9eb9e87e | ||
|
0d258b9e96 | ||
|
d4fa61e435 | ||
|
554301b8e9 | ||
|
e3ec0abca4 | ||
|
a55e848359 | ||
|
2653c3c4d4 | ||
|
4e4b65711a | ||
|
89c40ba256 | ||
|
e56ae6d651 | ||
|
64b2f18d92 | ||
|
2894f3c45c | ||
|
1bae2dcb00 | ||
|
b540c6df25 | ||
|
8b476e678a | ||
|
47f8a0cbe5 | ||
|
9459eef032 | ||
|
5aaa8924a6 | ||
|
4ffce1c297 | ||
|
0513660b05 | ||
|
3947c4de28 | ||
|
664228ef8b | ||
|
234a61a3ee | ||
|
83d1275d72 | ||
|
45abd7d59e | ||
|
88e10a9e54 | ||
|
1f624d4c2a | ||
|
9a25309c1c | ||
|
2b7caf6fcb | ||
|
938500fd2f | ||
|
2d26358602 | ||
|
9faa5d3aa4 | ||
|
bcc2a136c8 | ||
|
dcad002057 | ||
|
51b8d2ab25 | ||
|
aa3eda70e5 | ||
|
2d0860fe1b | ||
|
17877366b7 | ||
|
5eb3dee59c | ||
|
247bd19f93 | ||
|
e5ed115288 | ||
|
03baa471d9 | ||
|
da8b3d143a | ||
|
20616ff954 | ||
|
f463cf8662 | ||
|
e9879eac91 | ||
|
47148e78a3 | ||
|
fea917c8f2 | ||
|
948e16e3c7 | ||
|
1e7fc46406 | ||
|
71494264ca | ||
|
3125561474 | ||
|
abf1228a0a | ||
|
547a9d947b | ||
|
f696d98270 | ||
|
44ab7f995d | ||
|
92a1214dcd | ||
|
388f8da97f | ||
|
e96623588c | ||
|
e1e316b931 | ||
|
c4fd29541b | ||
|
a8b13f5a82 | ||
|
86fc573d7a | ||
|
f82e47f2af | ||
|
75a38fa38b | ||
|
ac62a3713c | ||
|
ea52c99560 | ||
|
7d4100ed8a | ||
|
c8d1d9c51f | ||
|
ab2eb3442e | ||
|
dbbeb7d4ed | ||
|
bfa00ffbdc | ||
|
10eb1a3aee | ||
|
fd2bf9f6f6 | ||
|
fc1f9f0124 | ||
|
789492d1a9 | ||
|
afc320bed4 | ||
|
cba4a6f9c8 | ||
|
5556d42b46 | ||
|
7d2bc1c7a4 | ||
|
c209c874a9 | ||
|
dd52e9cd72 | ||
|
0f71a150ff | ||
|
74388d4bf4 | ||
|
e811dd15b5 | ||
|
f22700a4ec | ||
|
ee25a7d458 | ||
|
594e212b8a | ||
|
5c355468c1 | ||
|
d1571d18f9 | ||
|
cb86b44dea | ||
|
833e6e0c92 | ||
|
159273e532 | ||
|
631f2db1a3 | ||
|
a1f0eaab54 | ||
|
b1898f6d8b | ||
|
d29e9e7e07 | ||
|
360605e284 | ||
|
64772544fa | ||
|
5f4875e2ba | ||
|
fd2a8c01cb | ||
|
b6129ad081 | ||
|
28d0b0a21e | ||
|
b779c43126 | ||
|
4e12725616 | ||
|
570bc9f32d | ||
|
47976caa69 | ||
|
f1abd8577c | ||
|
423b50b068 | ||
|
bedf0789f2 | ||
|
a2d1cb3b22 | ||
|
e7ba88f905 | ||
|
50f6e5232e | ||
|
8854f0a5ed | ||
|
13cc3acdfd | ||
|
5b7368c46d | ||
|
27999f2d26 | ||
|
e74f2a4274 | ||
|
d141a73493 | ||
|
631f44331e | ||
|
b01fddef8b | ||
|
78a8553f1c | ||
|
b70c1abd7c | ||
|
71f31d74bc | ||
|
72e557b3a9 | ||
|
3eaccbb61a | ||
|
b678bd8ed2 | ||
|
2baa140615 | ||
|
e760f07767 | ||
|
ca5d5f0a49 | ||
|
69b4fd49fc | ||
|
36f315c344 | ||
|
8016f5ca2d | ||
|
8ef2ff1257 | ||
|
6ce31defb6 | ||
|
0b3ad9141b | ||
|
c903db5c53 | ||
|
8a038b946c | ||
|
ed9740b72c | ||
|
ce3dcc9856 | ||
|
4472d50eba | ||
|
a1d5702ba0 | ||
|
52a231ce2f | ||
|
16af981772 | ||
|
fc141b8dfc | ||
|
82ebd29ff6 | ||
|
f5737b5a49 | ||
|
55e68d2c63 | ||
|
479f45da9b | ||
|
140c4e4812 | ||
|
337460d299 | ||
|
e41c3a7c92 | ||
|
1b9faa4d61 | ||
|
9f9a754a64 | ||
|
f7b0cfe8d1 | ||
|
4ce8667b9d | ||
|
0785b24192 | ||
|
34f02cf83d | ||
|
8b43d79257 | ||
|
dc086c6bf1 | ||
|
dc0ffbe16e | ||
|
8df66f7991 | ||
|
39580381fa | ||
|
06a0f278e5 | ||
|
fd97470f35 | ||
|
11fc3f1e56 | ||
|
693bb708b2 | ||
|
97719fe8da | ||
|
4933ace50b | ||
|
81bf0a6430 | ||
|
24aed1b14e | ||
|
09c3ac02f8 | ||
|
45a39585b3 | ||
|
286a455fa9 | ||
|
bdf4960b6a | ||
|
13124aafe3 | ||
|
2128be8b28 | ||
|
175294fa5d | ||
|
5016c7cf7c | ||
|
1468206052 | ||
|
d1ff4b5cf0 | ||
|
700a065abd | ||
|
3fc462f90f | ||
|
65a8f6bb4b | ||
|
e0f0944bff | ||
|
c7590dd752 | ||
|
33a5680328 | ||
|
455d87cadd | ||
|
e65ae09789 | ||
|
dacdce1ea2 | ||
|
766f3c50ec | ||
|
802b97b2ed | ||
|
2ad408f2b8 | ||
|
c8fddd8f62 | ||
|
74fab994e2 | ||
|
3a99542f73 | ||
|
2ae9f3555b | ||
|
1937b4bf66 | ||
|
8beb290842 | ||
|
8fec5155c7 | ||
|
3f49decce9 | ||
|
e2492f65a0 | ||
|
5116a812fb | ||
|
82409c060f | ||
|
183ed3f88f | ||
|
447ac3c40b | ||
|
96f96927da | ||
|
2c79e29483 | ||
|
362473dde0 | ||
|
8339682112 | ||
|
3f06d767b5 | ||
|
2854dc9ab3 | ||
|
516fcf9168 | ||
|
12d1971b0d | ||
|
3e09e509d6 | ||
|
99fb11cc72 | ||
|
d176ddd27e | ||
|
0847b3a4bf | ||
|
6d2ea8dae3 | ||
|
47ba4502e0 | ||
|
6263cb6456 | ||
|
d7024d80ce | ||
|
59d6b852af | ||
|
e512928df5 | ||
|
a38029e3e7 | ||
|
b12b652a57 | ||
|
c793416495 | ||
|
01343f187b | ||
|
69021e1497 | ||
|
08c408aae0 | ||
|
bf698659b0 | ||
|
2d5d70693a | ||
|
a4091ade5c | ||
|
a7a881809f | ||
|
0f4cb94cfe | ||
|
6469e9734b | ||
|
5372caefb3 | ||
|
9d7b973a62 | ||
|
a5f221aa39 | ||
|
e764b75aef | ||
|
708cfef3f7 | ||
|
01b8432d58 | ||
|
9b5b652c71 | ||
|
981a4778a9 | ||
|
5d4363d58d | ||
|
48697b8225 | ||
|
1652bb67d9 | ||
|
1499f681a3 | ||
|
a21fa6ff40 | ||
|
186dc3c1ab | ||
|
6b7a27d080 | ||
|
c9017f9f7a | ||
|
fdada8b3d4 | ||
|
9e2ff7d037 | ||
|
65834fc420 | ||
|
f5a4965de7 | ||
|
db5911eb73 | ||
|
2f4e63b443 | ||
|
584164a6ff | ||
|
e6f6d26d13 | ||
|
67d8d5848c | ||
|
43d15f09f0 | ||
|
7123c7ae3a | ||
|
4d9c346a17 | ||
|
deed9d3943 | ||
|
652f0c7676 | ||
|
e479ec8fb6 | ||
|
a33501e6a7 | ||
|
47f8199a99 | ||
|
4bf0ddc0e1 | ||
|
4cd0ccce83 | ||
|
a3657188b6 | ||
|
77b687d501 |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
dev = ["run", "--", "dev"]
|
@ -1,7 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*.rs]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
28
.github/workflows/rust.yml
vendored
28
.github/workflows/rust.yml
vendored
@ -1,10 +1,16 @@
|
||||
name: Rustlings Tests
|
||||
name: Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- website
|
||||
- '*.md'
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- website
|
||||
- '*.md'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@ -14,30 +20,28 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: cargo clippy -- --deny warnings
|
||||
- name: Clippy
|
||||
run: cargo clippy -- --deny warnings
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DavidAnson/markdownlint-cli2-action@v16
|
||||
with:
|
||||
globs: "exercises/**/*.md"
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt --all -- --check
|
||||
- name: rustfmt
|
||||
run: cargo fmt --all --check
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: swatinem/rust-cache@v2
|
||||
- name: Run cargo test
|
||||
run: cargo test
|
||||
- name: cargo test
|
||||
run: cargo test --workspace
|
||||
dev-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: swatinem/rust-cache@v2
|
||||
- name: Run rustlings dev check
|
||||
run: cargo run -- dev check --require-solutions
|
||||
- name: rustlings dev check
|
||||
run: cargo dev check --require-solutions
|
||||
|
87
.github/workflows/web.yml
vendored
87
.github/workflows/web.yml
vendored
@ -1,87 +0,0 @@
|
||||
# Workflow to build your docs with oranda (and mdbook)
|
||||
# and deploy them to Github Pages
|
||||
name: Web
|
||||
|
||||
# We're going to push to the gh-pages branch, so we need that permission
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
# What situations do we want to build docs in?
|
||||
# All of these work independently and can be removed / commented out
|
||||
# if you don't want oranda/mdbook running in that situation
|
||||
on:
|
||||
# Check that a PR didn't break docs!
|
||||
#
|
||||
# Note that the "Deploy to Github Pages" step won't run in this mode,
|
||||
# so this won't have any side-effects. But it will tell you if a PR
|
||||
# completely broke oranda/mdbook. Sadly we don't provide previews (yet)!
|
||||
pull_request:
|
||||
|
||||
# Whenever something gets pushed to main, update the docs!
|
||||
# This is great for getting docs changes live without cutting a full release.
|
||||
#
|
||||
# Note that if you're using cargo-dist, this will "race" the Release workflow
|
||||
# that actually builds the Github Release that oranda tries to read (and
|
||||
# this will almost certainly complete first). As a result you will publish
|
||||
# docs for the latest commit but the oranda landing page won't know about
|
||||
# the latest release. The workflow_run trigger below will properly wait for
|
||||
# cargo-dist, and so this half-published state will only last for ~10 minutes.
|
||||
#
|
||||
# If you only want docs to update with releases, disable this, or change it to
|
||||
# a "release" branch. You can, of course, also manually trigger a workflow run
|
||||
# when you want the docs to update.
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
# Whenever a workflow called "Release" completes, update the docs!
|
||||
#
|
||||
# If you're using cargo-dist, this is recommended, as it will ensure that
|
||||
# oranda always sees the latest release right when it's available. Note
|
||||
# however that Github's UI is wonky when you use workflow_run, and won't
|
||||
# show this workflow as part of any commit. You have to go to the "actions"
|
||||
# tab for your repo to see this one running (the gh-pages deploy will also
|
||||
# only show up there).
|
||||
workflow_run:
|
||||
workflows: [ "Release" ]
|
||||
types:
|
||||
- completed
|
||||
|
||||
# Alright, let's do it!
|
||||
jobs:
|
||||
web:
|
||||
name: Build and deploy site and docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Setup
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: swatinem/rust-cache@v2
|
||||
|
||||
# If you use any mdbook plugins, here's the place to install them!
|
||||
|
||||
# Install and run oranda (and mdbook)
|
||||
# This will write all output to ./public/ (including copying mdbook's output to there)
|
||||
- name: Install and run oranda
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/oranda/releases/download/v0.3.1/oranda-installer.sh | sh
|
||||
oranda build
|
||||
|
||||
# Deploy to our gh-pages branch (creating it if it doesn't exist)
|
||||
# the "public" dir that oranda made above will become the root dir
|
||||
# of this branch.
|
||||
#
|
||||
# Note that once the gh-pages branch exists, you must
|
||||
# go into repo's settings > pages and set "deploy from branch: gh-pages"
|
||||
# the other defaults work fine.
|
||||
- name: Deploy to Github Pages
|
||||
uses: JamesIves/github-pages-deploy-action@v4.4.1
|
||||
# ONLY if we're on main (so no PRs or feature branches allowed!)
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
with:
|
||||
branch: gh-pages
|
||||
# Gotta tell the action where to find oranda's output
|
||||
folder: public
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
single-commit: true
|
43
.github/workflows/website.yml
vendored
Normal file
43
.github/workflows/website.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
name: Website
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
paths: [website]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: website
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install TailwindCSS
|
||||
run: npm install
|
||||
- name: Build CSS
|
||||
run: npx @tailwindcss/cli -m -i input.css -o static/main.css
|
||||
- name: Download Zola
|
||||
run: curl -fsSL https://github.com/getzola/zola/releases/download/v0.20.0/zola-v0.20.0-x86_64-unknown-linux-gnu.tar.gz | tar xz
|
||||
- name: Build site
|
||||
run: ./zola build
|
||||
- name: Upload static files as artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: website/public/
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
|
||||
permissions:
|
||||
pages: write # to deploy to Pages
|
||||
id-token: write # to verify the deployment originates from an appropriate source
|
||||
# Deploy to the github-pages environment
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: actions/deploy-pages@v4
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -6,10 +6,6 @@ Cargo.lock
|
||||
# State file
|
||||
.rustlings-state.txt
|
||||
|
||||
# oranda
|
||||
public/
|
||||
.netlify
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
.direnv/
|
||||
|
@ -1,2 +0,0 @@
|
||||
# MD013/line-length Line length, Expected: 80
|
||||
MD013: false
|
@ -1,7 +1,7 @@
|
||||
[default.extend-words]
|
||||
"earch" = "earch" # Because of <s>earch in the list footer
|
||||
|
||||
[files]
|
||||
extend-exclude = [
|
||||
"CHANGELOG.md",
|
||||
]
|
||||
|
||||
[default.extend-words]
|
||||
"ratatui" = "ratatui"
|
||||
|
198
CHANGELOG.md
198
CHANGELOG.md
@ -1,12 +1,116 @@
|
||||
<a name="6.0.1"></a>
|
||||
## Unreleased
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to Rust edition 2024
|
||||
- Raise the minimum supported Rust version to `1.87`
|
||||
|
||||
## 6.4.0 (2024-11-11)
|
||||
|
||||
### Added
|
||||
|
||||
- The list of exercises is now searchable by pressing `s` or `/` 🔍️ (thanks to [@frroossst](https://github.com/frroossst))
|
||||
- New option `c` in the prompt to manually check all exercises ✅ (thanks to [@Nahor](https://github.com/Nahor))
|
||||
- New command `check-all` to manually check all exercises ✅ (thanks to [@Nahor](https://github.com/Nahor))
|
||||
- Addictive animation for showing the progress of checking all exercises. A nice showcase of parallelism in Rust ✨
|
||||
- New option `x` in the prompt to reset the file of the current exercise 🔄
|
||||
- Allow `dead_code` for all exercises and solutions ⚰️ (thanks to [@huss4in](https://github.com/huss4in))
|
||||
- Pause input while running an exercise to avoid unexpected prompt interactions ⏸️
|
||||
- Limit the maximum number of exercises to 999. Any community exercises willing to reach that limit? 🔝
|
||||
|
||||
### Changed
|
||||
|
||||
- `enums3`: Remove redundant enum definition task (thanks to [@senekor](https://github.com/senekor))
|
||||
- `if2`: Make the exercise less confusing by avoiding "fizz", "fuzz", "foo", "bar" and "baz" (thanks to [@senekor](https://github.com/senekor))
|
||||
- `hashmap3`: Use the method `Entry::or_default`.
|
||||
- Update the state of all exercises when checking all of them (thanks to [@Nahor](https://github.com/Nahor))
|
||||
- The main prompt doesn't need a confirmation with ENTER on Unix-like systems anymore.
|
||||
- No more jumping back to a previous exercise when its file is changed. Use the list to jump between exercises.
|
||||
- Dump the solution file after an exercise is done even if the solution's directory doesn't exist.
|
||||
- Rework the footer in the list.
|
||||
- Optimize the file watcher.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix bad contrast in the list on terminals with a light theme.
|
||||
|
||||
## 6.3.0 (2024-08-29)
|
||||
|
||||
### Added
|
||||
|
||||
- Add the following exercise lints:
|
||||
- `forbid(unsafe_code)`: You shouldn't write unsafe code in Rustlings.
|
||||
- `forbid(unstable_features)`: You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust.
|
||||
- `forbid(todo)`: You forgot a `todo!()`.
|
||||
- `forbid(empty_loop)`: This can only happen by mistake in Rustlings.
|
||||
- `deny(infinite_loop)`: No infinite loops are needed in Rustlings.
|
||||
- `deny(mem_forget)`: You shouldn't leak memory while still learning Rust.
|
||||
- Show a link to every exercise file in the list.
|
||||
- Add scroll padding in the list.
|
||||
- Break the help footer of the list into two lines when the terminal width isn't big enough.
|
||||
- Enable scrolling with the mouse in the list.
|
||||
- `dev check`: Show the progress of checks.
|
||||
- `dev check`: Check that the length of all exercise names is lower than 32.
|
||||
- `dev check`: Check if exercise contains no tests and isn't marked with `test = false`.
|
||||
|
||||
### Changed
|
||||
|
||||
- The compilation time when installing Rustlings is reduced.
|
||||
- Pressing `c` in the list for "continue on" now quits the list after setting the selected exercise as the current one.
|
||||
- Better highlighting of the solution file after an exercise is done.
|
||||
- Don't show the output of successful tests anymore. Instead, show the pretty output for tests.
|
||||
- Be explicit about `q` only quitting the list and not the whole program in the list.
|
||||
- Be explicit about `r` only resetting one exercise (the selected one) in the list.
|
||||
- Ignore the standard output of `git init`.
|
||||
- `threads3`: Remove the queue length and improve tests.
|
||||
- `errors4`: Use match instead of a comparison chain in the solution.
|
||||
- `functions3`: Only take `u8` to avoid using a too high number of iterations by mistake.
|
||||
- `dev check`: Always check with strict Clippy (warnings to errors) when checking the solutions.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix the error on some systems about too many open files during the final check of all exercises.
|
||||
- Fix the list when the terminal height is too low.
|
||||
- Restore the terminal after an error in the list.
|
||||
|
||||
## 6.2.0 (2024-08-09)
|
||||
|
||||
### Added
|
||||
|
||||
- Show a message before checking and running an exercise. This gives the user instant feedback and avoids confusion if the checks take too long.
|
||||
- Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports.
|
||||
- Add a `README.md` file to the `solutions/` directory.
|
||||
- Allow initializing Rustlings in a Cargo workspace.
|
||||
- `dev check`: Check that all solutions are formatted with `rustfmt`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove the state file and the solutions directory from the generated `.gitignore` file.
|
||||
- Run the final check of all exercises in parallel.
|
||||
- Small exercise improvements.
|
||||
|
||||
## 6.1.0 (2024-07-10)
|
||||
|
||||
#### Added
|
||||
|
||||
- `dev check`: Check that all exercises (including community ones) include at least one `TODO` comment.
|
||||
- `dev check`: Check that all exercises actually fail to run (not already solved).
|
||||
|
||||
#### Changed
|
||||
|
||||
- Make enum variants more consistent between enum exercises.
|
||||
- `iterators3`: Teach about the possible case of integer overflow during division.
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Exit with a helpful error message on missing/unsupported terminal/TTY.
|
||||
- Mark the last exercise as done.
|
||||
|
||||
## 6.0.1 (2024-07-04)
|
||||
|
||||
Small exercise improvements and fixes.
|
||||
Most importantly, fixed that the exercise `clippy1` was already solved 😅
|
||||
|
||||
<a name="6.0.0"></a>
|
||||
|
||||
## 6.0.0 (2024-07-03)
|
||||
|
||||
This release is the result of a complete rewrite to deliver a ton of new features and improvements ✨
|
||||
@ -35,7 +139,7 @@ You can read about the motivations of this change in [this issue](https://github
|
||||
|
||||
### List mode
|
||||
|
||||
A list mode was added using [Ratatui](https://ratatui.rs).
|
||||
A new list mode was added!
|
||||
You can enter it by entering `l` in the watch mode.
|
||||
It offers the following features:
|
||||
|
||||
@ -64,15 +168,13 @@ This should avoid issues related to the language server or to running exercises,
|
||||
Clippy lints are now shown on all exercises, not only the Clippy exercises 📎
|
||||
Make Clippy your friend from early on 🥰
|
||||
|
||||
### Third party exercises
|
||||
### Community Exercises
|
||||
|
||||
Rustlings now supports third-party exercises!
|
||||
Rustlings now supports community exercises!
|
||||
|
||||
Do you want to create your own set of Rustlings exercises to focus on some specific topic?
|
||||
Or do you want to translate the original Rustlings exercises?
|
||||
Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)!
|
||||
|
||||
<a name="5.6.1"></a>
|
||||
Then follow the link to the guide about [community exercises](https://rustlings.rust-lang.org/community-exercises)!
|
||||
|
||||
## 5.6.1 (2023-09-18)
|
||||
|
||||
@ -89,8 +191,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- `as_ref_mut`: Fixed a typo in a test function name.
|
||||
- `enums3`: Fixed formatting with `rustfmt`.
|
||||
|
||||
<a name="5.6.0"></a>
|
||||
|
||||
## 5.6.0 (2023-09-04)
|
||||
|
||||
#### Added
|
||||
@ -130,16 +230,12 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Lots of Nix housekeeping that I don't feel qualified to write about!
|
||||
- Improved CI workflows, we're now testing on multiple platforms at once.
|
||||
|
||||
<a name="5.5.1"></a>
|
||||
|
||||
## 5.5.1 (2023-05-17)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Reverted `rust-project.json` path generation due to an upstream `rust-analyzer` fix.
|
||||
|
||||
<a name="5.5.0"></a>
|
||||
|
||||
## 5.5.0 (2023-05-17)
|
||||
|
||||
#### Added
|
||||
@ -174,8 +270,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Added a markdown linter to run on GitHub actions
|
||||
- Split quick installation section into two code blocks
|
||||
|
||||
<a name="5.4.1"></a>
|
||||
|
||||
## 5.4.1 (2023-03-10)
|
||||
|
||||
#### Changed
|
||||
@ -191,8 +285,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- `macros4`: Prevented auto-fix by adding `#[rustfmt::skip]`
|
||||
- `cli`: Actually show correct progress percentages
|
||||
|
||||
<a name="5.4.0"></a>
|
||||
|
||||
## 5.4.0 (2023-02-12)
|
||||
|
||||
#### Changed
|
||||
@ -221,8 +313,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
|
||||
- Bumped min Rust version to 1.58 in installation script
|
||||
|
||||
<a name="5.3.0"></a>
|
||||
|
||||
## 5.3.0 (2022-12-23)
|
||||
|
||||
#### Added
|
||||
@ -255,8 +345,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Applied some Clippy and rustfmt formatting
|
||||
- Added a note on Windows PowerShell and other shell compatibility
|
||||
|
||||
<a name="5.2.1"></a>
|
||||
|
||||
## 5.2.1 (2022-09-06)
|
||||
|
||||
#### Fixed
|
||||
@ -270,8 +358,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
|
||||
- Fixed a typo in README.md
|
||||
|
||||
<a name="5.2.0"></a>
|
||||
|
||||
## 5.2.0 (2022-08-27)
|
||||
|
||||
#### Added
|
||||
@ -288,16 +374,12 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- **quiz1**: Adjusted the explanations to be consistent with
|
||||
the tests
|
||||
|
||||
<a name="5.1.1"></a>
|
||||
|
||||
## 5.1.1 (2022-08-17)
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
- Fixed an incorrect assertion in options1
|
||||
|
||||
<a name="5.1.0"></a>
|
||||
|
||||
## 5.1.0 (2022-08-16)
|
||||
|
||||
#### Features
|
||||
@ -332,8 +414,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Clarified manual installation instructions using `cargo install --path .`
|
||||
- Added a link to our Zulip in the readme file
|
||||
|
||||
<a name="5.0.0"></a>
|
||||
|
||||
## 5.0.0 (2022-07-16)
|
||||
|
||||
#### Features
|
||||
@ -406,8 +486,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Updated spacing in Cargo.toml.
|
||||
- Added a GitHub actions config so that tests run on every PR/commit.
|
||||
|
||||
<a name="4.8.0"></a>
|
||||
|
||||
## 4.8.0 (2022-07-01)
|
||||
|
||||
#### Features
|
||||
@ -428,8 +506,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Replaced the git.io URL with the fully qualified URL because of git.io's sunsetting.
|
||||
- Removed the deprecated Rust GitPod extension.
|
||||
|
||||
<a name="4.7.1"></a>
|
||||
|
||||
## 4.7.1 (2022-04-20)
|
||||
|
||||
#### Features
|
||||
@ -450,8 +526,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- The changelog will now be manually written instead of being automatically generated by the
|
||||
Git log.
|
||||
|
||||
<a name="4.7.0"></a>
|
||||
|
||||
## 4.7.0 (2022-04-14)
|
||||
|
||||
#### Features
|
||||
@ -492,8 +566,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Add hints on how to get GCC installed (#741) ([bc56861](https://github.com/rust-lang/rustlings/commit/bc5686174463ad6f4f6b824b0e9b97c3039d4886))
|
||||
- Fix some code blocks that were not highlighted ([17f9d74](https://github.com/rust-lang/rustlings/commit/17f9d7429ccd133a72e815fb5618e0ce79560929))
|
||||
|
||||
<a name="4.6.0"></a>
|
||||
|
||||
## 4.6.0 (2021-09-25)
|
||||
|
||||
#### Features
|
||||
@ -516,8 +588,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Clarify instructions ([df25684c](https://github.com/rust-lang/rustlings/commit/df25684cb79f8413915e00b5efef29369849cef1))
|
||||
- **quiz1:** Fix inconsistent wording (#826) ([03131a3d](https://github.com/rust-lang/rustlings/commit/03131a3d35d9842598150f9da817f7cc26e2669a))
|
||||
|
||||
<a name="4.5.0"></a>
|
||||
|
||||
## 4.5.0 (2021-07-07)
|
||||
|
||||
#### Features
|
||||
@ -538,8 +608,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- **try_from_into, from_str:** hints for dyn Error ([11d2cf0d](https://github.com/rust-lang/rustlings/commit/11d2cf0d604dee3f5023c17802d69438e69fa50e))
|
||||
- **variables5:** confine the answer further ([48ffcbd2](https://github.com/rust-lang/rustlings/commit/48ffcbd2c4cc4d936c2c7480019190f179813cc5))
|
||||
|
||||
<a name="4.4.0"></a>
|
||||
|
||||
## 4.4.0 (2021-04-24)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -581,8 +649,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- updated progress percentage ([1c6f7e4b](https://github.com/rust-lang/rustlings/commit/1c6f7e4b7b9b3bd36f4da2bb2b69c549cc8bd913))
|
||||
- added progress info ([c0e3daac](https://github.com/rust-lang/rustlings/commit/c0e3daacaf6850811df5bc57fa43e0f249d5cfa4))
|
||||
|
||||
<a name="4.3.0"></a>
|
||||
|
||||
## 4.3.0 (2020-12-29)
|
||||
|
||||
#### Features
|
||||
@ -605,8 +671,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Update description (#584) ([96347df9](https://github.com/rust-lang/rustlings/commit/96347df9df294f01153b29d9ad4ba361f665c755))
|
||||
- **vec1:** Have test compare every element in a and v ([9b6c6293](https://github.com/rust-lang/rustlings/commit/9b6c629397b24b944f484f5b2bbd8144266b5695))
|
||||
|
||||
<a name="4.2.0"></a>
|
||||
|
||||
## 4.2.0 (2020-11-07)
|
||||
|
||||
#### Features
|
||||
@ -627,8 +691,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- missing comma in test ([4fb230da](https://github.com/rust-lang/rustlings/commit/4fb230daf1251444fcf29e085cee222a91f8a37e))
|
||||
- **quiz3:** Second test is for odd numbers, not even. (#553) ([18e0bfef](https://github.com/rust-lang/rustlings/commit/18e0bfef1de53071e353ba1ec5837002ff7290e6))
|
||||
|
||||
<a name="4.1.0"></a>
|
||||
|
||||
## 4.1.0 (2020-10-05)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -651,8 +713,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- **cli:** Added 'cls' command to 'watch' mode (#474) ([4f2468e1](https://github.com/rust-lang/rustlings/commit/4f2468e14f574a93a2e9b688367b5752ed96ae7b))
|
||||
- **try_from_into:** Add insufficient length test (#469) ([523d18b8](https://github.com/rust-lang/rustlings/commit/523d18b873a319f7c09262f44bd40e2fab1830e5))
|
||||
|
||||
<a name="4.0.0"></a>
|
||||
|
||||
## 4.0.0 (2020-07-08)
|
||||
|
||||
#### Breaking Changes
|
||||
@ -694,8 +754,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- **test2:** name of type String and &str (#394) ([d6c0a688](https://github.com/rust-lang/rustlings/commit/d6c0a688e6a96f93ad60d540d4b326f342fc0d45))
|
||||
- **variables6:** minor typo (#419) ([524e17df](https://github.com/rust-lang/rustlings/commit/524e17df10db95f7b90a0f75cc8997182a8a4094))
|
||||
|
||||
<a name="3.0.0"></a>
|
||||
|
||||
## 3.0.0 (2020-04-11)
|
||||
|
||||
#### Breaking Changes
|
||||
@ -718,8 +776,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- add new exercises for generics (#280) ([76be5e4e](https://github.com/rust-lang/rustlings/commit/76be5e4e991160f5fd9093f03ee2ba260e8f7229))
|
||||
- **ci:** add buildkite config ([b049fa2c](https://github.com/rust-lang/rustlings/commit/b049fa2c84dba0f0c8906ac44e28fd45fba51a71))
|
||||
|
||||
<a name="2.2.1"></a>
|
||||
|
||||
### 2.2.1 (2020-02-27)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -730,13 +786,11 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
|
||||
- Add clippy lints (#269) ([1e2fd9c9](https://github.com/rust-lang/rustlings/commit/1e2fd9c92f8cd6e389525ca1a999fca4c90b5921))
|
||||
|
||||
<a name="2.2.0"></a>
|
||||
|
||||
## 2.2.0 (2020-02-25)
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
- Update deps to version compatable with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401))
|
||||
- Update deps to version compatible with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401))
|
||||
- **docs:**
|
||||
- Added a necessary step to Windows installation process (#242) ([3906efcd](https://github.com/rust-lang/rustlings/commit/3906efcd52a004047b460ed548037093de3f523f))
|
||||
- Fixed mangled sentence from book; edited for clarity (#266) ([ade52ff](https://github.com/rust-lang/rustlings/commit/ade52ffb739987287ddd5705944c8777705faed9))
|
||||
@ -759,8 +813,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Added traits exercises (#274 but specifically #216, which originally added
|
||||
this :heart:) ([b559cdd](https://github.com/rust-lang/rustlings/commit/b559cdd73f32c0d0cfc1feda39f82b3e3583df17))
|
||||
|
||||
<a name="2.1.0"></a>
|
||||
|
||||
## 2.1.0 (2019-11-27)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -778,8 +830,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
|
||||
- **watch:** show hint while watching ([8143d57b](https://github.com/rust-lang/rustlings/commit/8143d57b4e88c51341dd4a18a14c536042cc009c))
|
||||
|
||||
<a name="2.0.0"></a>
|
||||
|
||||
## 2.0.0 (2019-11-12)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -800,8 +850,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- **cli:** check for rustc before doing anything ([36a033b8](https://github.com/rust-lang/rustlings/commit/36a033b87a6549c1e5639c908bf7381c84f4f425))
|
||||
- **hint:** Add test for hint ([ce9fa6eb](https://github.com/rust-lang/rustlings/commit/ce9fa6ebbfdc3e7585d488d9409797285708316f))
|
||||
|
||||
<a name="1.5.1"></a>
|
||||
|
||||
### 1.5.1 (2019-11-11)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -813,8 +861,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- **threads:** Move Threads behind SLT ([fbe91a67](https://github.com/rust-lang/rustlings/commit/fbe91a67a482bfe64cbcdd58d06ba830a0f39da3), closes [#205](https://github.com/rust-lang/rustlings/issues/205))
|
||||
- **watch:** clear screen before each `verify()` ([3aff590](https://github.com/rust-lang/rustlings/commit/3aff59085586c24196a547c2693adbdcf4432648))
|
||||
|
||||
<a name="1.5.0"></a>
|
||||
|
||||
## 1.5.0 (2019-11-09)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -839,8 +885,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Added exercise for struct update syntax ([1c4c8764](https://github.com/rust-lang/rustlings/commit/1c4c8764ed118740cd4cee73272ddc6cceb9d959))
|
||||
- **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031))
|
||||
|
||||
<a name="1.4.1"></a>
|
||||
|
||||
### 1.4.1 (2019-08-13)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -849,8 +893,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- **option1:** Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6))
|
||||
- **test1:** Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446))
|
||||
|
||||
<a name="1.4.0"></a>
|
||||
|
||||
## 1.4.0 (2019-07-13)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -867,8 +909,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- **changelog:** Use clog for changelogs ([34e31232](https://github.com/rust-lang/rustlings/commit/34e31232dfddde284a341c9609b33cd27d9d5724))
|
||||
- **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031))
|
||||
|
||||
<a name="1.3.0"></a>
|
||||
|
||||
### 1.3.0 (2019-06-05)
|
||||
|
||||
#### Features
|
||||
@ -884,16 +924,12 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Fix broken link (#164, @HanKruiger)
|
||||
- Remove highlighting and syntect (#167, @komaeda)
|
||||
|
||||
<a name="1.2.2"></a>
|
||||
|
||||
### 1.2.2 (2019-05-07)
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
- Reverted `--nocapture` flag since it was causing tests to pass unconditionally
|
||||
|
||||
<a name="1.2.1"></a>
|
||||
|
||||
### 1.2.1 (2019-04-22)
|
||||
|
||||
#### Bug Fixes
|
||||
@ -901,8 +937,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Fix the `--nocapture` feature (@komaeda)
|
||||
- Provide a nicer error message for when you're in the wrong directory
|
||||
|
||||
<a name="1.2.0"></a>
|
||||
|
||||
### 1.2.0 (2019-04-22)
|
||||
|
||||
#### Features
|
||||
@ -910,8 +944,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Add errors to exercises that compile without user changes (@yvan-sraka)
|
||||
- Use --nocapture when testing, enabling `println!` when running (@komaeda)
|
||||
|
||||
<a name="1.1.1"></a>
|
||||
|
||||
### 1.1.1 (2019-04-14)
|
||||
|
||||
#### Bug fixes
|
||||
@ -924,8 +956,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Fix links by deleting book version (@diodfr, #142)
|
||||
- Canonicalize paths to fix path matching (@cjpearce, #143)
|
||||
|
||||
<a name="1.1.0"></a>
|
||||
|
||||
### 1.1.0 (2019-03-20)
|
||||
|
||||
- errors2.rs: update link to Rust book (#124)
|
||||
@ -935,16 +965,12 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER
|
||||
- Give a warning when Rustlings isn't run from the right directory (#123)
|
||||
- Verify that rust version is recent enough to install Rustlings (#131)
|
||||
|
||||
<a name="1.0.1"></a>
|
||||
|
||||
### 1.0.1 (2019-03-06)
|
||||
|
||||
- Adds a way to install Rustlings in one command (`curl -L https://git.io/rustlings | bash`)
|
||||
- Makes `rustlings watch` react to create file events (@shaunbennett, #117)
|
||||
- Reworks the exercise management to use an external TOML file instead of just listing them in the code
|
||||
|
||||
<a name="1.0.0"></a>
|
||||
|
||||
### 1.0.0 (2019-03-06)
|
||||
|
||||
Initial release.
|
||||
|
799
Cargo.lock
generated
799
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
58
Cargo.toml
58
Cargo.toml
@ -1,27 +1,26 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
exclude = [
|
||||
"tests/fixture/failure",
|
||||
"tests/fixture/state",
|
||||
"tests/fixture/success",
|
||||
"tests/test_exercises",
|
||||
"dev",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "6.0.1"
|
||||
version = "6.4.0"
|
||||
authors = [
|
||||
"Liv <mokou@fastmail.com>",
|
||||
"Mo Bitar <mo8it@proton.me>",
|
||||
"Mo Bitar <mo8it@proton.me>", # https://github.com/mo8it
|
||||
"Liv <mokou@fastmail.com>", # https://github.com/shadows-withal
|
||||
# Alumni
|
||||
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>",
|
||||
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>", # https://github.com/carols10cents
|
||||
]
|
||||
repository = "https://github.com/rust-lang/rustlings"
|
||||
license = "MIT"
|
||||
edition = "2021"
|
||||
edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and `CARGO_TOML` in `dev new`.
|
||||
rust-version = "1.87"
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml_edit = { version = "0.22", default-features = false, features = ["parse", "serde"] }
|
||||
|
||||
[package]
|
||||
name = "rustlings"
|
||||
@ -31,6 +30,7 @@ authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
keywords = [
|
||||
"exercise",
|
||||
"learning",
|
||||
@ -46,21 +46,20 @@ include = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
clap = { version = "4.5.8", features = ["derive"] }
|
||||
crossterm = "0.27.0"
|
||||
hashbrown = "0.14.5"
|
||||
notify-debouncer-mini = { version = "0.4.1", default-features = false }
|
||||
os_pipe = "1.2.0"
|
||||
ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] }
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.0.1" }
|
||||
serde_json = "1.0.120"
|
||||
anyhow = "1.0"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
crossterm = { version = "0.29", default-features = false, features = ["windows", "events"] }
|
||||
notify = "8.0"
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" }
|
||||
serde_json = "1.0"
|
||||
serde.workspace = true
|
||||
toml_edit.workspace = true
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0.14"
|
||||
predicates = "3.1.0"
|
||||
tempfile = "3.19"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
@ -70,3 +69,20 @@ panic = "abort"
|
||||
|
||||
[package.metadata.release]
|
||||
pre-release-hook = ["./release-hook.sh"]
|
||||
pre-release-commit-message = "Release 🎉"
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
unstable_features = "forbid"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
empty_loop = "forbid"
|
||||
disallowed-types = "deny"
|
||||
disallowed-methods = "deny"
|
||||
infinite_loop = "deny"
|
||||
mem_forget = "deny"
|
||||
dbg_macro = "warn"
|
||||
todo = "warn"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
144
README.md
144
README.md
@ -1,143 +1,7 @@
|
||||
<div class="oranda-hide">
|
||||
# [Rustlings](https://rustlings.rust-lang.org) 🦀
|
||||
|
||||
# Rustlings 🦀❤️
|
||||
Small exercises to get you used to reading and writing [Rust](https://www.rust-lang.org) code - _Recommended in parallel to reading [the official Rust book](https://doc.rust-lang.org/book) 📚️_
|
||||
|
||||
</div>
|
||||
Visit the **website** for a demo, info about setup and more:
|
||||
|
||||
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!
|
||||
|
||||
It is recommended to do the Rustlings exercises in parallel to reading [the official Rust book](https://doc.rust-lang.org/book/), the most comprehensive resource for learning Rust 📚️
|
||||
|
||||
[Rust By Example](https://doc.rust-lang.org/rust-by-example/) is another recommended resource that you might find helpful.
|
||||
It contains code examples and exercises similar to Rustlings, but online.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installing Rust
|
||||
|
||||
Before installing Rustlings, you need to have _Rust installed_.
|
||||
Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust.
|
||||
This'll also install _Cargo_, Rust's package/project manager.
|
||||
|
||||
> 🐧 If you're on Linux, make sure you've installed `gcc` (for a linker).
|
||||
>
|
||||
> Deb: `sudo apt install gcc`.
|
||||
> Dnf: `sudo dnf install gcc`.
|
||||
|
||||
> 🍎 If you're on MacOS, make sure you've installed Xcode and its developer tools by running `xcode-select --install`.
|
||||
|
||||
### Installing Rustlings
|
||||
|
||||
The following command will download and compile Rustlings:
|
||||
|
||||
```bash
|
||||
cargo install rustlings
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><strong>If the installation fails…</strong> (<em>click to expand</em>)</summary>
|
||||
|
||||
- Make sure you have the latest Rust version by running `rustup update`
|
||||
- Try adding the `--locked` flag: `cargo install rustlings --locked`
|
||||
- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
|
||||
|
||||
</details>
|
||||
|
||||
### Initialization
|
||||
|
||||
After installing Rustlings, run the following command to initialize the `rustlings/` directory:
|
||||
|
||||
```bash
|
||||
rustlings init
|
||||
```
|
||||
|
||||
Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises:
|
||||
|
||||
```bash
|
||||
cd rustlings/
|
||||
rustlings
|
||||
```
|
||||
|
||||
## Working environment
|
||||
|
||||
### Editor
|
||||
|
||||
Our general recommendation is [VS Code](https://code.visualstudio.com/) with the [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer).
|
||||
But any editor that supports [rust-analyzer](https://rust-analyzer.github.io/) should be enough for working on the exercises.
|
||||
|
||||
### Terminal
|
||||
|
||||
While working with Rustlings, please use a modern terminal for the best user experience.
|
||||
The default terminal on Linux and Mac should be sufficient.
|
||||
On Windows, we recommend the [Windows Terminal](https://aka.ms/terminal).
|
||||
|
||||
If you use VS Code, the builtin terminal should also be fine.
|
||||
|
||||
## Doing exercises
|
||||
|
||||
The exercises are sorted by topic and can be found in the subdirectory `exercises/<topic>`.
|
||||
For every topic, there is an additional `README.md` file with some resources to get you started on the topic.
|
||||
We highly recommend that you have a look at them before you start 📚️
|
||||
|
||||
Most exercises contain an error that keeps them from compiling, and it's up to you to fix it!
|
||||
Some exercises contain tests that need to pass for the exercise to be done ✅
|
||||
|
||||
Search for `TODO` and `todo!()` to find out what you need to change.
|
||||
Ask for hints by entering `h` in the _watch mode_ 💡
|
||||
|
||||
### Watch Mode
|
||||
|
||||
After [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`.
|
||||
|
||||
This will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers).
|
||||
It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory.
|
||||
|
||||
<details>
|
||||
<summary><strong>If detecting file changes in the <code>exercises/</code> directory fails…</strong> (<em>click to expand</em>)</summary>
|
||||
|
||||
> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode.
|
||||
>
|
||||
> Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL).
|
||||
|
||||
</details>
|
||||
|
||||
### Exercise List
|
||||
|
||||
In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` to open the interactive exercise list.
|
||||
|
||||
The list allows you to…
|
||||
|
||||
- See the status of all exercises (done or pending)
|
||||
- `c`: Continue at another exercise (temporarily skip some exercises or go back to a previous one)
|
||||
- `r`: Reset status and file of an exercise (you need to _reload/reopen_ its file in your editor afterwards)
|
||||
|
||||
See the footer of the list for all possible keys.
|
||||
|
||||
## Continuing On
|
||||
|
||||
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.
|
||||
|
||||
## Third-Party Exercises
|
||||
|
||||
Do you want to create your own set of Rustlings exercises to focus on some specific topic?
|
||||
Or do you want to translate the original Rustlings exercises?
|
||||
Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)!
|
||||
|
||||
## Uninstalling Rustlings
|
||||
|
||||
If you want to remove Rustlings from your system, run the following command:
|
||||
|
||||
```bash
|
||||
cargo uninstall rustlings
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md) 🔗
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) 🎉
|
||||
## ➡️ [rustlings.rust-lang.org](https://rustlings.rust-lang.org) ⬅️
|
||||
|
@ -1,53 +0,0 @@
|
||||
# Third-Party Exercises
|
||||
|
||||
The support of Rustlings for third-party exercises allows you to create your own set of Rustlings exercises to focus on some specific topic.
|
||||
You could also offer a translatation of the original Rustlings exercises as third-party exercises.
|
||||
|
||||
## Getting started
|
||||
|
||||
To create third-party exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`.
|
||||
This command will, similar to `cargo new PROJECT_NAME`, create a template directory called `PROJECT_NAME` with all what you need to get started.
|
||||
|
||||
Read the comments in the generated `info.toml` file to understand its format.
|
||||
It allows you to set a custom welcome and final message and specify the metadata of every exercise.
|
||||
|
||||
## Create an exercise
|
||||
|
||||
Here is an example of the metadata of one file:
|
||||
|
||||
```toml
|
||||
[[exercises]]
|
||||
name = "intro1"
|
||||
hint = """
|
||||
To finish this exercise, you need to …
|
||||
This link might help you …"""
|
||||
```
|
||||
|
||||
After entering this in `info.toml`, create the file `intro1.rs` in the `exercises/` directory.
|
||||
The exercise needs to contain a `main` function, but it can be empty.
|
||||
Adding tests is recommended.
|
||||
Look at the official Rustlings exercises for inspiration.
|
||||
|
||||
You can optionally add a solution file `intro1.rs` to the `solutions/` directory.
|
||||
|
||||
Now, run `rustlings dev check`.
|
||||
It will tell you about any issues with your exercises.
|
||||
For example, it will tell you to run `rustlings dev update` to update the `Cargo.toml` file to include the new exercise `intro1`.
|
||||
|
||||
`rustlings dev check` will also run your solutions (if you have any) to make sure that they run successfully.
|
||||
|
||||
That's it!
|
||||
You finished your first exercise 🎉
|
||||
|
||||
## Publish
|
||||
|
||||
Now, add more exercises and publish them as a Git repository.
|
||||
|
||||
Users just have to clone that repository and run `rustlings` in it to start working on your set of exercises just like the official ones.
|
||||
|
||||
One difference to the official exercises is that the solution files will not be hidden until the user finishes an exercise.
|
||||
But you can trust the users to not look at the solution too early 😉
|
||||
|
||||
## Share
|
||||
|
||||
After publishing your set of exercises, open an issue or a pull request in the official Rustlings repository to link to your project in the README 😃
|
5
build.rs
Normal file
5
build.rs
Normal file
@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
// Fix building from source on Windows because it can't handle file links.
|
||||
#[cfg(windows)]
|
||||
let _ = std::fs::copy("dev/Cargo.toml", "dev-Cargo.toml");
|
||||
}
|
15
clippy.toml
Normal file
15
clippy.toml
Normal file
@ -0,0 +1,15 @@
|
||||
disallowed-types = [
|
||||
# Inefficient. Use `.queue(…)` instead.
|
||||
"crossterm::style::Stylize",
|
||||
"crossterm::style::styled_content::StyledContent",
|
||||
]
|
||||
|
||||
disallowed-methods = [
|
||||
# Inefficient. Use `.queue(…)` instead.
|
||||
"crossterm::style::style",
|
||||
# Use `thread::Builder::spawn` instead and handle the error.
|
||||
"std::thread::spawn",
|
||||
"std::thread::Scope::spawn",
|
||||
# Return `ExitCode` instead.
|
||||
"std::process::exit",
|
||||
]
|
@ -1,4 +1,4 @@
|
||||
# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update`. This comment line will be stripped in `rustlings init`.
|
||||
# Don't edit the `bin` list manually! It is updated by `cargo dev update`. This comment line will be stripped in `rustlings init`.
|
||||
bin = [
|
||||
{ name = "intro1", path = "../exercises/00_intro/intro1.rs" },
|
||||
{ name = "intro1_sol", path = "../solutions/00_intro/intro1.rs" },
|
||||
@ -192,6 +192,32 @@ bin = [
|
||||
|
||||
[package]
|
||||
name = "exercises"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
# Don't publish the exercises on crates.io!
|
||||
publish = false
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[lints.rust]
|
||||
# You shouldn't write unsafe code in Rustlings!
|
||||
unsafe_code = "forbid"
|
||||
# You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust.
|
||||
unstable_features = "forbid"
|
||||
# Dead code warnings can't be avoided in some exercises and might distract while learning.
|
||||
dead_code = "allow"
|
||||
|
||||
[lints.clippy]
|
||||
# You forgot a `todo!()`!
|
||||
todo = "forbid"
|
||||
# This can only happen by mistake in Rustlings.
|
||||
empty_loop = "forbid"
|
||||
# No infinite loops are needed in Rustlings.
|
||||
infinite_loop = "deny"
|
||||
# You shouldn't leak memory while still learning Rust!
|
||||
mem_forget = "deny"
|
||||
# Currently, there are no disallowed methods. This line avoids problems when developing Rustlings.
|
||||
disallowed_methods = "allow"
|
||||
|
@ -1 +1 @@
|
||||
This file is used to check if the user tries to run Rustlings in the repository (the method before v6)
|
||||
This file is used to check if the user tries to run Rustlings in the repository (the method before version 6)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// TODO: We sometimes encourage you to keep trying things on a given exercise,
|
||||
// TODO: We sometimes encourage you to keep trying things on a given exercise
|
||||
// even after you already figured it out. If you got everything working and feel
|
||||
// ready for the next exercise, enter `n` in the terminal.
|
||||
//
|
||||
@ -6,8 +6,7 @@
|
||||
// Try adding a new `println!` and check the updated output in the terminal.
|
||||
|
||||
fn main() {
|
||||
println!("Hello and");
|
||||
println!(r#" welcome to... "#);
|
||||
println!(r#" Welcome to... "#);
|
||||
println!(r#" _ _ _ "#);
|
||||
println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
|
||||
println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Variables
|
||||
|
||||
In Rust, variables are immutable by default.
|
||||
When a variable is immutable, once a value is bound to a name, you can’t change that value.
|
||||
When a variable is immutable, once a value is bound to a name, you can't change that value.
|
||||
You can make them mutable by adding `mut` in front of the variable name.
|
||||
|
||||
## Further information
|
||||
|
@ -1,5 +1,5 @@
|
||||
fn main() {
|
||||
// TODO: Add missing keyword.
|
||||
// TODO: Add the missing keyword.
|
||||
x = 5;
|
||||
|
||||
println!("x has the value {x}");
|
||||
|
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
let number = "T-H-R-E-E"; // Don't change this line
|
||||
println!("Spell a number: {}", number);
|
||||
println!("Spell a number: {number}");
|
||||
|
||||
// TODO: Fix the compiler error by changing the line below without renaming the variable.
|
||||
number = 3;
|
||||
|
@ -1,4 +1,4 @@
|
||||
fn call_me(num: u32) {
|
||||
fn call_me(num: u8) {
|
||||
for i in 0..num {
|
||||
println!("Ring! Call number {}", i + 1);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// TODO: Fix the compiler error on this function.
|
||||
fn foo_if_fizz(fizzish: &str) -> &str {
|
||||
if fizzish == "fizz" {
|
||||
"foo"
|
||||
fn picky_eater(food: &str) -> &str {
|
||||
if food == "strawberry" {
|
||||
"Yummy!"
|
||||
} else {
|
||||
1
|
||||
}
|
||||
@ -18,18 +18,20 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn foo_for_fizz() {
|
||||
// This means that calling `foo_if_fizz` with the argument "fizz" should return "foo".
|
||||
assert_eq!(foo_if_fizz("fizz"), "foo");
|
||||
fn yummy_food() {
|
||||
// This means that calling `picky_eater` with the argument "strawberry" should return "Yummy!".
|
||||
assert_eq!(picky_eater("strawberry"), "Yummy!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bar_for_fuzz() {
|
||||
assert_eq!(foo_if_fizz("fuzz"), "bar");
|
||||
fn neutral_food() {
|
||||
assert_eq!(picky_eater("potato"), "I guess I can eat that.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_to_baz() {
|
||||
assert_eq!(foo_if_fizz("literally anything"), "baz");
|
||||
fn default_disliked_food() {
|
||||
assert_eq!(picky_eater("broccoli"), "No thanks!");
|
||||
assert_eq!(picky_eater("gummy bears"), "No thanks!");
|
||||
assert_eq!(picky_eater("literally anything"), "No thanks!");
|
||||
}
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ compiler. In this section, we'll go through the most important ones.
|
||||
|
||||
## Further information
|
||||
|
||||
- [Data Types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html)
|
||||
- [The Slice Type](https://doc.rust-lang.org/stable/book/ch04-03-slices.html)
|
||||
- [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html)
|
||||
- [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html)
|
||||
|
@ -12,6 +12,6 @@ the other useful data structure, hash maps, later.
|
||||
|
||||
## Further information
|
||||
|
||||
- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html)
|
||||
- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/book/ch08-01-vectors.html)
|
||||
- [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut)
|
||||
- [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map)
|
||||
|
@ -7,12 +7,12 @@ mod tests {
|
||||
// TODO: Fix the compiler errors only by reordering the lines in the test.
|
||||
// Don't add, change or remove any line.
|
||||
#[test]
|
||||
fn move_semantics5() {
|
||||
let mut x = 100;
|
||||
fn move_semantics4() {
|
||||
let mut x = Vec::new();
|
||||
let y = &mut x;
|
||||
let z = &mut x;
|
||||
*y += 100;
|
||||
*z += 1000;
|
||||
assert_eq!(x, 1200);
|
||||
y.push(42);
|
||||
z.push(13);
|
||||
assert_eq!(x, [42, 13]);
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,6 @@
|
||||
// TODO: Fix the compiler errors without changing anything except adding or
|
||||
// removing references (the character `&`).
|
||||
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
get_char(data);
|
||||
|
||||
string_uppercase(&data);
|
||||
}
|
||||
|
||||
// Shouldn't take ownership
|
||||
fn get_char(data: String) -> char {
|
||||
data.chars().last().unwrap()
|
||||
@ -22,3 +14,11 @@ fn string_uppercase(mut data: &String) {
|
||||
|
||||
println!("{data}");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
get_char(data);
|
||||
|
||||
string_uppercase(&data);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Structs contain data, but can also have logic. In this exercise we have
|
||||
// defined the `Package` struct and we want to test some logic attached to it.
|
||||
// Structs contain data, but can also have logic. In this exercise, we have
|
||||
// defined the `Package` struct, and we want to test some logic attached to it.
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Package {
|
||||
|
@ -1,10 +1,10 @@
|
||||
# Enums
|
||||
|
||||
Rust allows you to define types called "enums" which enumerate possible values.
|
||||
Enums are a feature in many languages, but their capabilities differ in each language. Rust’s enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell.
|
||||
Enums are a feature in many languages, but their capabilities differ in each language. Rust's enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell.
|
||||
Useful in combination with enums is Rust's "pattern matching" facility, which makes it easy to run different code for different values of an enumeration.
|
||||
|
||||
## Further information
|
||||
|
||||
- [Enums](https://doc.rust-lang.org/book/ch06-00-enums.html)
|
||||
- [Pattern syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html)
|
||||
- [Pattern syntax](https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html)
|
||||
|
@ -4,8 +4,9 @@ enum Message {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", Message::Quit);
|
||||
println!("{:?}", Message::Echo);
|
||||
println!("{:?}", Message::Resize);
|
||||
println!("{:?}", Message::Move);
|
||||
println!("{:?}", Message::Echo);
|
||||
println!("{:?}", Message::ChangeColor);
|
||||
println!("{:?}", Message::Quit);
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
// TODO: Define the different variants used below.
|
||||
@ -12,7 +17,11 @@ impl Message {
|
||||
|
||||
fn main() {
|
||||
let messages = [
|
||||
Message::Move { x: 10, y: 30 },
|
||||
Message::Resize {
|
||||
width: 10,
|
||||
height: 30,
|
||||
},
|
||||
Message::Move(Point { x: 10, y: 15 }),
|
||||
Message::Echo(String::from("hello world")),
|
||||
Message::ChangeColor(200, 255, 255),
|
||||
Message::Quit,
|
||||
|
@ -1,40 +1,51 @@
|
||||
enum Message {
|
||||
// TODO: Implement the message variant types based on their usage below.
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x: u8,
|
||||
y: u8,
|
||||
enum Message {
|
||||
Resize { width: u64, height: u64 },
|
||||
Move(Point),
|
||||
Echo(String),
|
||||
ChangeColor(u8, u8, u8),
|
||||
Quit,
|
||||
}
|
||||
|
||||
struct State {
|
||||
color: (u8, u8, u8),
|
||||
width: u64,
|
||||
height: u64,
|
||||
position: Point,
|
||||
quit: bool,
|
||||
message: String,
|
||||
// RGB color composed of red, green and blue.
|
||||
color: (u8, u8, u8),
|
||||
quit: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn change_color(&mut self, color: (u8, u8, u8)) {
|
||||
self.color = color;
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
self.quit = true;
|
||||
}
|
||||
|
||||
fn echo(&mut self, s: String) {
|
||||
self.message = s;
|
||||
fn resize(&mut self, width: u64, height: u64) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
}
|
||||
|
||||
fn move_position(&mut self, point: Point) {
|
||||
self.position = point;
|
||||
}
|
||||
|
||||
fn echo(&mut self, s: String) {
|
||||
self.message = s;
|
||||
}
|
||||
|
||||
fn change_color(&mut self, red: u8, green: u8, blue: u8) {
|
||||
self.color = (red, green, blue);
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
self.quit = true;
|
||||
}
|
||||
|
||||
fn process(&mut self, message: Message) {
|
||||
// TODO: Create a match expression to process the different message variants.
|
||||
// Remember: When passing a tuple as a function argument, you'll need extra parentheses:
|
||||
// e.g. `foo((t, u, p, l, e))`
|
||||
// TODO: Create a match expression to process the different message
|
||||
// variants using the methods defined above.
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,21 +60,29 @@ mod tests {
|
||||
#[test]
|
||||
fn test_match_message_call() {
|
||||
let mut state = State {
|
||||
quit: false,
|
||||
width: 0,
|
||||
height: 0,
|
||||
position: Point { x: 0, y: 0 },
|
||||
color: (0, 0, 0),
|
||||
message: String::from("hello world"),
|
||||
color: (0, 0, 0),
|
||||
quit: false,
|
||||
};
|
||||
|
||||
state.process(Message::ChangeColor(255, 0, 255));
|
||||
state.process(Message::Echo(String::from("Hello world!")));
|
||||
state.process(Message::Resize {
|
||||
width: 10,
|
||||
height: 30,
|
||||
});
|
||||
state.process(Message::Move(Point { x: 10, y: 15 }));
|
||||
state.process(Message::Echo(String::from("Hello world!")));
|
||||
state.process(Message::ChangeColor(255, 0, 255));
|
||||
state.process(Message::Quit);
|
||||
|
||||
assert_eq!(state.color, (255, 0, 255));
|
||||
assert_eq!(state.width, 10);
|
||||
assert_eq!(state.height, 30);
|
||||
assert_eq!(state.position.x, 10);
|
||||
assert_eq!(state.position.y, 15);
|
||||
assert!(state.quit);
|
||||
assert_eq!(state.message, "Hello world!");
|
||||
assert_eq!(state.color, (255, 0, 255));
|
||||
assert!(state.quit);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ mod tests {
|
||||
assert_eq!(trim_me("Hello! "), "Hello!");
|
||||
assert_eq!(trim_me(" What's up!"), "What's up!");
|
||||
assert_eq!(trim_me(" Hola! "), "Hola!");
|
||||
assert_eq!(trim_me("Hi!"), "Hi!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -4,6 +4,7 @@ fn placeholder() {}
|
||||
fn string_slice(arg: &str) {
|
||||
println!("{arg}");
|
||||
}
|
||||
|
||||
fn string(arg: String) {
|
||||
println!("{arg}");
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
// You can bring module paths into scopes and provide new names for them with
|
||||
// the `use` and `as` keywords.
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod delicious_snacks {
|
||||
// TODO: Add the following two `use` statements after fixing them.
|
||||
// use self::fruits::PEAR as ???;
|
||||
|
@ -5,7 +5,8 @@
|
||||
// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You
|
||||
// must add fruit to the basket so that there is at least one of each kind and
|
||||
// more than 11 in total - we have a lot of mouths to feed. You are not allowed
|
||||
// to insert any more of these fruits!
|
||||
// to insert any more of the fruits that are already in the basket (Apple,
|
||||
// Mango, and Lychee).
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -10,14 +10,14 @@ use std::collections::HashMap;
|
||||
|
||||
// A structure to store the goal details of a team.
|
||||
#[derive(Default)]
|
||||
struct Team {
|
||||
struct TeamScores {
|
||||
goals_scored: u8,
|
||||
goals_conceded: u8,
|
||||
}
|
||||
|
||||
fn build_scores_table(results: &str) -> HashMap<&str, Team> {
|
||||
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
|
||||
// The name of the team is the key and its associated struct is the value.
|
||||
let mut scores = HashMap::new();
|
||||
let mut scores = HashMap::<&str, TeamScores>::new();
|
||||
|
||||
for line in results.lines() {
|
||||
let mut split_iterator = line.split(',');
|
||||
|
@ -14,7 +14,7 @@ Option types are very common in Rust code, as they have a number of uses:
|
||||
|
||||
## Further Information
|
||||
|
||||
- [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions)
|
||||
- [Option Enum Format](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions)
|
||||
- [Option Module Documentation](https://doc.rust-lang.org/std/option/)
|
||||
- [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html)
|
||||
- [if let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html)
|
||||
|
@ -2,7 +2,7 @@
|
||||
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
|
||||
// someone eats it all, so no ice cream is left (value 0). Return `None` if
|
||||
// `hour_of_day` is higher than 23.
|
||||
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
|
||||
fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {
|
||||
// TODO: Complete the function body.
|
||||
}
|
||||
|
||||
@ -18,19 +18,19 @@ mod tests {
|
||||
fn raw_value() {
|
||||
// TODO: Fix this test. How do you get the value contained in the
|
||||
// Option?
|
||||
let icecreams = maybe_icecream(12);
|
||||
let ice_creams = maybe_ice_cream(12);
|
||||
|
||||
assert_eq!(icecreams, 5); // Don't change this line.
|
||||
assert_eq!(ice_creams, 5); // Don't change this line.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_icecream() {
|
||||
assert_eq!(maybe_icecream(0), Some(5));
|
||||
assert_eq!(maybe_icecream(9), Some(5));
|
||||
assert_eq!(maybe_icecream(18), Some(5));
|
||||
assert_eq!(maybe_icecream(22), Some(0));
|
||||
assert_eq!(maybe_icecream(23), Some(0));
|
||||
assert_eq!(maybe_icecream(24), None);
|
||||
assert_eq!(maybe_icecream(25), None);
|
||||
fn check_ice_cream() {
|
||||
assert_eq!(maybe_ice_cream(0), Some(5));
|
||||
assert_eq!(maybe_ice_cream(9), Some(5));
|
||||
assert_eq!(maybe_ice_cream(18), Some(5));
|
||||
assert_eq!(maybe_ice_cream(22), Some(0));
|
||||
assert_eq!(maybe_ice_cream(23), Some(0));
|
||||
assert_eq!(maybe_ice_cream(24), None);
|
||||
assert_eq!(maybe_ice_cream(25), None);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ fn main() {
|
||||
|
||||
// TODO: Fix the compiler error by adding something to this match statement.
|
||||
match optional_point {
|
||||
Some(p) => println!("Co-ordinates are {},{}", p.x, p.y),
|
||||
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
|
||||
_ => panic!("No match!"),
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Error handling
|
||||
|
||||
Most errors aren’t serious enough to require the program to stop entirely.
|
||||
Sometimes, when a function fails, it’s for a reason that you can easily interpret and respond to.
|
||||
For example, if you try to open a file and that operation fails because the file doesn’t exist, you might want to create the file instead of terminating the process.
|
||||
Most errors aren't serious enough to require the program to stop entirely.
|
||||
Sometimes, when a function fails, it's for a reason that you can easily interpret and respond to.
|
||||
For example, if you try to open a file and that operation fails because the file doesn't exist, you might want to create the file instead of terminating the process.
|
||||
|
||||
## Further information
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
// of `Option<String>`.
|
||||
fn generate_nametag_text(name: String) -> Option<String> {
|
||||
if name.is_empty() {
|
||||
// Empty names aren't allowed.
|
||||
// Empty names aren't allowed
|
||||
None
|
||||
} else {
|
||||
Some(format!("Hi! My name is {name}"))
|
||||
|
@ -19,6 +19,7 @@ fn main() {
|
||||
let mut tokens = 100;
|
||||
let pretend_user_input = "8";
|
||||
|
||||
// Don't change this line.
|
||||
let cost = total_cost(pretend_user_input)?;
|
||||
|
||||
if cost > tokens {
|
||||
|
@ -1,5 +1,3 @@
|
||||
#![allow(clippy::comparison_chain)]
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
|
@ -6,7 +6,7 @@
|
||||
//
|
||||
// In short, this particular use case for boxes is for when you want to own a
|
||||
// value and you care only that it is a type which implements a particular
|
||||
// trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where
|
||||
// trait. To do so, the `Box` is declared as of type `Box<dyn Trait>` where
|
||||
// `Trait` is the trait the compiler looks for on any value used in that
|
||||
// context. For this exercise, that context is the potential errors which
|
||||
// can be returned in a `Result`.
|
||||
@ -39,8 +39,8 @@ struct PositiveNonzeroInteger(u64);
|
||||
impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
||||
match value {
|
||||
0 => Err(CreationError::Zero),
|
||||
x if x < 0 => Err(CreationError::Negative),
|
||||
0 => Err(CreationError::Zero),
|
||||
x => Ok(PositiveNonzeroInteger(x as u64)),
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ impl ParsePosNonzeroError {
|
||||
}
|
||||
|
||||
// TODO: Add another error conversion function here.
|
||||
// fn from_parseint(???) -> Self { ??? }
|
||||
// fn from_parse_int(???) -> Self { ??? }
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
|
@ -7,5 +7,5 @@ The simplest and most common use of generics is for type parameters.
|
||||
|
||||
## Further information
|
||||
|
||||
- [Generic Data Types](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html)
|
||||
- [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html)
|
||||
- [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html)
|
||||
|
@ -1,5 +1,3 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
trait Licensed {
|
||||
// TODO: Add a default implementation for `licensing_info` so that
|
||||
// implementors like the two structs below can share that default behavior
|
||||
|
@ -9,7 +9,7 @@ impl Rectangle {
|
||||
if width <= 0 || height <= 0 {
|
||||
// Returning a `Result` would be better here. But we want to learn
|
||||
// how to test functions that can panic.
|
||||
panic!("Rectangle width and height can't be negative");
|
||||
panic!("Rectangle width and height must be positive");
|
||||
}
|
||||
|
||||
Rectangle { width, height }
|
||||
|
@ -1,12 +1,16 @@
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum DivisionError {
|
||||
// Example: 42 / 0
|
||||
DivideByZero,
|
||||
// Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
|
||||
IntegerOverflow,
|
||||
// Example: 5 / 2 = 2.5
|
||||
NotDivisible,
|
||||
}
|
||||
|
||||
// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
|
||||
// Otherwise, return a suitable error.
|
||||
fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
|
||||
fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
@ -42,6 +46,11 @@ mod tests {
|
||||
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integer_overflow() {
|
||||
assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_divisible() {
|
||||
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));
|
||||
|
@ -1,5 +1,8 @@
|
||||
fn factorial(num: u8) -> u64 {
|
||||
// TODO: Complete this function to return the factorial of `num`.
|
||||
fn factorial(num: u64) -> u64 {
|
||||
// TODO: Complete this function to return the factorial of `num` which is
|
||||
// defined as `1 * 2 * 3 * … * num`.
|
||||
// https://en.wikipedia.org/wiki/Factorial
|
||||
//
|
||||
// Do not use:
|
||||
// - early returns (using the `return` keyword explicitly)
|
||||
// Try not to use:
|
||||
|
@ -45,8 +45,9 @@ mod tests {
|
||||
#[test]
|
||||
fn owned_no_mutation() {
|
||||
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
|
||||
// case, no mutation occurs and thus also no clone. But the result is
|
||||
// still owned because it was never borrowed or mutated.
|
||||
// case, no mutation occurs (all numbers are already absolute) and thus
|
||||
// also no clone. But the result is still owned because it was never
|
||||
// borrowed or mutated.
|
||||
let vec = vec![0, 1, 2];
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
@ -56,9 +57,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn owned_mutation() {
|
||||
// Of course this is also the case if a mutation does occur. In this
|
||||
// case, the call to `to_mut()` in the `abs_all` function returns a
|
||||
// reference to the same data as before.
|
||||
// Of course this is also the case if a mutation does occur (not all
|
||||
// numbers are absolute). In this case, the call to `to_mut()` in the
|
||||
// `abs_all` function returns a reference to the same data as before.
|
||||
let vec = vec![-1, 0, 1];
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
|
@ -8,7 +8,6 @@ use std::rc::Rc;
|
||||
#[derive(Debug)]
|
||||
struct Sun;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
enum Planet {
|
||||
Mercury(Rc<Sun>),
|
||||
|
@ -1,5 +1,5 @@
|
||||
// This program spawns multiple threads that each run for at least 250ms, and
|
||||
// each thread returns how much time they took to complete. The program should
|
||||
// This program spawns multiple threads that each runs for at least 250ms, and
|
||||
// each thread returns how much time it took to complete. The program should
|
||||
// wait until all the spawned threads have finished and should collect their
|
||||
// return values into a vector.
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::{sync::mpsc, thread, time::Duration};
|
||||
|
||||
struct Queue {
|
||||
length: u32,
|
||||
first_half: Vec<u32>,
|
||||
second_half: Vec<u32>,
|
||||
}
|
||||
@ -9,7 +8,6 @@ struct Queue {
|
||||
impl Queue {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
length: 10,
|
||||
first_half: vec![1, 2, 3, 4, 5],
|
||||
second_half: vec![6, 7, 8, 9, 10],
|
||||
}
|
||||
@ -48,17 +46,15 @@ mod tests {
|
||||
fn threads3() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let queue = Queue::new();
|
||||
let queue_length = queue.length;
|
||||
|
||||
send_tx(queue, tx);
|
||||
|
||||
let mut total_received: u32 = 0;
|
||||
for received in rx {
|
||||
println!("Got: {received}");
|
||||
total_received += 1;
|
||||
let mut received = Vec::with_capacity(10);
|
||||
for value in rx {
|
||||
received.push(value);
|
||||
}
|
||||
|
||||
println!("Number of received values: {total_received}");
|
||||
assert_eq!(total_received, queue_length);
|
||||
received.sort();
|
||||
assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
}
|
||||
}
|
||||
|
@ -10,5 +10,6 @@ of exercises to Rustlings, but is all about learning to write Macros.
|
||||
|
||||
## Further information
|
||||
|
||||
- [Macros](https://doc.rust-lang.org/book/ch19-06-macros.html)
|
||||
- [The Rust Book - Macros](https://doc.rust-lang.org/book/ch20-05-macros.html)
|
||||
- [The Little Book of Rust Macros](https://veykril.github.io/tlborm/)
|
||||
- [Rust by Example - macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html)
|
||||
|
@ -4,9 +4,11 @@
|
||||
#[rustfmt::skip]
|
||||
#[allow(unused_variables, unused_assignments)]
|
||||
fn main() {
|
||||
let my_option: Option<()> = None;
|
||||
let my_option: Option<&str> = None;
|
||||
// Assume that you don't know the value of `my_option`.
|
||||
// In the case of `Some`, we want to print its value.
|
||||
if my_option.is_none() {
|
||||
println!("{:?}", my_option.unwrap());
|
||||
println!("{}", my_option.unwrap());
|
||||
}
|
||||
|
||||
let my_arr = &[
|
||||
|
@ -2,10 +2,11 @@
|
||||
// about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and
|
||||
// https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively.
|
||||
|
||||
// Obtain the number of bytes (not characters) in the given argument.
|
||||
// Obtain the number of bytes (not characters) in the given argument
|
||||
// (`.len()` returns the number of bytes in a string).
|
||||
// TODO: Add the `AsRef` trait appropriately as a trait bound.
|
||||
fn byte_counter<T>(arg: T) -> usize {
|
||||
arg.as_ref().as_bytes().len()
|
||||
arg.as_ref().len()
|
||||
}
|
||||
|
||||
// Obtain the number of characters (not bytes) in the given argument.
|
||||
|
@ -25,7 +25,7 @@ enum ParsePersonError {
|
||||
ParseInt(ParseIntError),
|
||||
}
|
||||
|
||||
// TODO: Complete this `From` implementation to be able to parse a `Person`
|
||||
// TODO: Complete this `FromStr` implementation to be able to parse a `Person`
|
||||
// out of a string in the form of "Mark,20".
|
||||
// Note that you'll need to parse the age component into a `u8` with something
|
||||
// like `"4".parse::<u8>()`.
|
||||
|
@ -5,12 +5,12 @@
|
||||
//
|
||||
// Mary is buying apples. The price of an apple is calculated as follows:
|
||||
// - An apple costs 2 rustbucks.
|
||||
// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck!
|
||||
// - However, if Mary buys more than 40 apples, the price of each apple in the
|
||||
// entire order is reduced to only 1 rustbuck!
|
||||
|
||||
// TODO: Write a function that calculates the price of an order of apples given
|
||||
// the quantity bought.
|
||||
|
||||
// Put your function here!
|
||||
// fn calculate_price_of_apples(???) -> ??? {
|
||||
// fn calculate_price_of_apples(???) -> ??? { ??? }
|
||||
|
||||
fn main() {
|
||||
// You can optionally experiment here.
|
||||
|
@ -26,7 +26,7 @@ enum Command {
|
||||
mod my_module {
|
||||
use super::Command;
|
||||
|
||||
// TODO: Complete the function.
|
||||
// TODO: Complete the function as described above.
|
||||
// pub fn transformer(input: ???) -> ??? { ??? }
|
||||
}
|
||||
|
||||
|
13
oranda.json
13
oranda.json
@ -1,13 +0,0 @@
|
||||
{
|
||||
"project": {
|
||||
"homepage": "https://rustlings.cool",
|
||||
"repository": "https://github.com/rust-lang/rustlings"
|
||||
},
|
||||
"marketing": {
|
||||
"analytics": {
|
||||
"plausible": {
|
||||
"domain": "rustlings.cool"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,14 @@
|
||||
# Error out if any command fails
|
||||
set -e
|
||||
|
||||
cargo run -- dev check
|
||||
typos
|
||||
cargo outdated -w --exit-code 1
|
||||
cargo test --workspace --all-targets
|
||||
cargo upgrades
|
||||
|
||||
# Similar to CI
|
||||
cargo clippy -- --deny warnings
|
||||
cargo fmt --all --check
|
||||
cargo test --workspace
|
||||
cargo dev check --require-solutions
|
||||
|
||||
# MSRV
|
||||
cargo +1.87 dev check --require-solutions
|
||||
|
@ -6,6 +6,7 @@ authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
include = [
|
||||
"/src/",
|
||||
"/info.toml",
|
||||
@ -15,6 +16,9 @@ include = [
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.36"
|
||||
quote = "1.0"
|
||||
serde.workspace = true
|
||||
toml_edit.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -1,6 +1,7 @@
|
||||
format_version = 1
|
||||
|
||||
welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners!
|
||||
welcome_message = """
|
||||
Is this your first time? Don't worry, Rustlings is made for beginners!
|
||||
We are going to teach you a lot of things about Rust, but before we can
|
||||
get started, here are some notes about how Rustlings operates:
|
||||
|
||||
@ -10,15 +11,16 @@ get started, here are some notes about how Rustlings operates:
|
||||
and fix them!
|
||||
2. Make sure to have your editor open in the `rustlings/` directory. Rustlings
|
||||
will show you the path of the current exercise under the progress bar. Open
|
||||
the exercise file in your editor, fix errors and save the file. Rustlings will
|
||||
automatically detect the file change and rerun the exercise. If all errors are
|
||||
fixed, Rustlings will ask you to move on to the next exercise.
|
||||
the exercise file in your editor, fix errors and save the file. Rustlings
|
||||
will automatically detect the file change and rerun the exercise. If all
|
||||
errors are fixed, Rustlings will ask you to move on to the next exercise.
|
||||
3. If you're stuck on an exercise, enter `h` to show a hint.
|
||||
4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub!
|
||||
(https://github.com/rust-lang/rustlings). We look at every issue, and sometimes,
|
||||
other learners do too so you can help each other out!"""
|
||||
4. If an exercise doesn't make sense to you, feel free to open an issue on
|
||||
GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and
|
||||
sometimes, other learners do too so you can help each other out!"""
|
||||
|
||||
final_message = """We hope you enjoyed learning about the various aspects of Rust!
|
||||
final_message = """
|
||||
We hope you enjoyed learning about the various aspects of Rust!
|
||||
If you noticed any issues, don't hesitate to report them on Github.
|
||||
You can also contribute your own exercises to help the greater community!
|
||||
|
||||
@ -31,6 +33,7 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"""
|
||||
name = "intro1"
|
||||
dir = "00_intro"
|
||||
test = false
|
||||
skip_check_unsolved = true
|
||||
hint = """
|
||||
Enter `n` to move on to the next exercise.
|
||||
You might need to press ENTER after typing `n`."""
|
||||
@ -119,10 +122,10 @@ dir = "01_variables"
|
||||
test = false
|
||||
hint = """
|
||||
We know about variables and mutability, but there is another important type of
|
||||
variables available: constants.
|
||||
variable available: constants.
|
||||
|
||||
Constants are always immutable. They are declared with the keyword `const` instead
|
||||
of `let`.
|
||||
Constants are always immutable. They are declared with the keyword `const`
|
||||
instead of `let`.
|
||||
|
||||
The type of Constants must always be annotated.
|
||||
|
||||
@ -252,7 +255,7 @@ require you to type in 100 items (but you certainly can if you want!).
|
||||
|
||||
For example, you can do:
|
||||
```
|
||||
let array = ["Are we there yet?"; 10];
|
||||
let array = ["Are we there yet?"; 100];
|
||||
```
|
||||
|
||||
Bonus: what are some other things you could have that would return `true`
|
||||
@ -308,7 +311,7 @@ In Rust, there are two ways to define a Vector.
|
||||
inside the square brackets. This way is simpler when you exactly know
|
||||
the initial values.
|
||||
|
||||
Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html
|
||||
Check this chapter: https://doc.rust-lang.org/book/ch08-01-vectors.html
|
||||
of the Rust book to learn more."""
|
||||
|
||||
[[exercises]]
|
||||
@ -318,7 +321,8 @@ hint = """
|
||||
In the first function, we create an empty vector and want to push new elements
|
||||
to it.
|
||||
|
||||
In the second function, we map the values of the input and collect them into a vector.
|
||||
In the second function, we map the values of the input and collect them into
|
||||
a vector.
|
||||
|
||||
After you've completed both functions, decide for yourself which approach you
|
||||
like better.
|
||||
@ -331,8 +335,8 @@ What do you think is the more commonly used pattern under Rust developers?"""
|
||||
name = "move_semantics1"
|
||||
dir = "06_move_semantics"
|
||||
hint = """
|
||||
So you've got the "cannot borrow `vec` as mutable, as it is not declared as mutable"
|
||||
error on the line where we push an element to the vector, right?
|
||||
So you've got the "cannot borrow `vec` as mutable, as it is not declared as
|
||||
mutable" error on the line where we push an element to the vector, right?
|
||||
|
||||
The fix for this is going to be adding one keyword, and the addition is NOT on
|
||||
the line where we push to the vector (where the error is).
|
||||
@ -350,7 +354,7 @@ We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's
|
||||
being "moved" into `vec1`, meaning we can't access `vec0` anymore.
|
||||
|
||||
You could make another, separate version of the data that's in `vec0` and
|
||||
pass it to `fill_vec` instead."""
|
||||
pass it to `fill_vec` instead. This is called cloning in Rust."""
|
||||
|
||||
[[exercises]]
|
||||
name = "move_semantics3"
|
||||
@ -368,7 +372,8 @@ hint = """
|
||||
Carefully reason about the range in which each mutable reference is in
|
||||
scope. Does it help to update the value of `x` immediately after
|
||||
the mutable reference is taken?
|
||||
Read more about 'Mutable References' in the book's section 'References and Borrowing':
|
||||
Read more about 'Mutable References' in the book's section 'References and
|
||||
Borrowing':
|
||||
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references."""
|
||||
|
||||
[[exercises]]
|
||||
@ -377,7 +382,7 @@ dir = "06_move_semantics"
|
||||
test = false
|
||||
hint = """
|
||||
To find the answer, you can consult the book section "References and Borrowing":
|
||||
https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html
|
||||
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html
|
||||
|
||||
The first problem is that `get_char` is taking ownership of the string. So
|
||||
`data` is moved and can't be used for `string_uppercase`. `data` is moved to
|
||||
@ -415,7 +420,7 @@ to its fields.
|
||||
|
||||
There are however some shortcuts that can be taken when instantiating structs.
|
||||
Have a look in The Book to find out more:
|
||||
https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
|
||||
https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
|
||||
|
||||
[[exercises]]
|
||||
name = "structs3"
|
||||
@ -444,7 +449,7 @@ dir = "08_enums"
|
||||
test = false
|
||||
hint = """
|
||||
You can create enumerations that have different variants with different types
|
||||
such as no data, anonymous structs, a single string, tuples, etc."""
|
||||
such as anonymous structs, structs, a single string, tuples, no data, etc."""
|
||||
|
||||
[[exercises]]
|
||||
name = "enums3"
|
||||
@ -486,7 +491,7 @@ to add one character to the `if` statement, though, that will coerce the
|
||||
Side note: If you're interested in learning about how this kind of reference
|
||||
conversion works, you can jump ahead in the book and read this part in the
|
||||
smart pointers chapter:
|
||||
https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods"""
|
||||
https://doc.rust-lang.org/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods"""
|
||||
|
||||
[[exercises]]
|
||||
name = "strings3"
|
||||
@ -497,14 +502,18 @@ some of them:
|
||||
https://doc.rust-lang.org/std/string/struct.String.html#method.trim
|
||||
|
||||
For the `compose_me` method: You can either use the `format!` macro, or convert
|
||||
the string slice into an owned string, which you can then freely extend."""
|
||||
the string slice into an owned string, which you can then freely extend.
|
||||
|
||||
For the `replace_me` method, you can check out the `replace` method:
|
||||
https://doc.rust-lang.org/std/string/struct.String.html#method.replace"""
|
||||
|
||||
[[exercises]]
|
||||
name = "strings4"
|
||||
dir = "09_strings"
|
||||
test = false
|
||||
hint = """
|
||||
Replace `placeholder` with either `string` or `string_slice` in the `main` function.
|
||||
Replace `placeholder` with either `string` or `string_slice` in the `main`
|
||||
function.
|
||||
|
||||
Example:
|
||||
`placeholder("blue");`
|
||||
@ -560,18 +569,14 @@ hint = """
|
||||
Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this.
|
||||
|
||||
Learn more in The Book:
|
||||
https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value"""
|
||||
https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value"""
|
||||
|
||||
[[exercises]]
|
||||
name = "hashmaps3"
|
||||
dir = "11_hashmaps"
|
||||
hint = """
|
||||
Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of
|
||||
`HashMap` to insert the default value of `Team` if a team doesn't
|
||||
exist in the table yet.
|
||||
|
||||
Learn more in The Book:
|
||||
https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
|
||||
Hint 1: Use the `entry()` and `or_default()` methods of `HashMap` to insert the
|
||||
default value of `TeamScores` if a team doesn't exist in the table yet.
|
||||
|
||||
Hint 2: If there is already an entry for a given key, the value returned by
|
||||
`entry()` can be updated based on the existing value.
|
||||
@ -584,7 +589,7 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-
|
||||
[[exercises]]
|
||||
name = "quiz2"
|
||||
dir = "quizzes"
|
||||
hint = "No hints this time ;)"
|
||||
hint = "The `+` operator can concatenate a `String` with a `&str`."
|
||||
|
||||
# OPTIONS
|
||||
|
||||
@ -738,7 +743,7 @@ name = "generics2"
|
||||
dir = "14_generics"
|
||||
hint = """
|
||||
Related section in The Book:
|
||||
https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions"""
|
||||
https://doc.rust-lang.org/book/ch10-01-syntax.html#in-method-definitions"""
|
||||
|
||||
# TRAITS
|
||||
|
||||
@ -747,7 +752,9 @@ name = "traits1"
|
||||
dir = "15_traits"
|
||||
hint = """
|
||||
More about traits in The Book:
|
||||
https://doc.rust-lang.org/book/ch10-02-traits.html"""
|
||||
https://doc.rust-lang.org/book/ch10-02-traits.html
|
||||
|
||||
The `+` operator can concatenate a `String` with a `&str`."""
|
||||
|
||||
[[exercises]]
|
||||
name = "traits2"
|
||||
@ -757,7 +764,7 @@ Notice how the trait takes ownership of `self` and returns `Self`.
|
||||
|
||||
Although the signature of `append_bar` in the trait takes `self` as argument,
|
||||
the implementation can take `mut self` instead. This is possible because the
|
||||
the value is owned anyway."""
|
||||
value is owned anyway."""
|
||||
|
||||
[[exercises]]
|
||||
name = "traits3"
|
||||
@ -868,7 +875,7 @@ We expect the method `Rectangle::new` to panic for negative values.
|
||||
To handle that, you need to add a special attribute to the test function.
|
||||
|
||||
You can refer to the docs:
|
||||
https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
|
||||
https://doc.rust-lang.org/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
|
||||
|
||||
# STANDARD LIBRARY TYPES
|
||||
|
||||
@ -887,9 +894,9 @@ hint = """
|
||||
`capitalize_first`:
|
||||
|
||||
The variable `first` is a `char`. It needs to be capitalized and added to the
|
||||
remaining characters in `c` in order to return the correct `String`.
|
||||
remaining characters in `chars` in order to return the correct `String`.
|
||||
|
||||
The remaining characters in `c` can be viewed as a string slice using the
|
||||
The remaining characters in `chars` can be viewed as a string slice using the
|
||||
`as_str` method.
|
||||
|
||||
The documentation for `char` contains many useful methods.
|
||||
@ -1004,7 +1011,7 @@ thread-local copy of the numbers.
|
||||
This is a simple exercise if you understand the underlying concepts, but if this
|
||||
is too much of a struggle, consider reading through all of Chapter 16 in The
|
||||
Book:
|
||||
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html"""
|
||||
https://doc.rust-lang.org/book/ch16-00-concurrency.html"""
|
||||
|
||||
[[exercises]]
|
||||
name = "cow1"
|
||||
@ -1133,7 +1140,7 @@ constants, but clippy recognizes those imprecise mathematical constants as a
|
||||
source of potential error.
|
||||
|
||||
See the suggestions of the Clippy warning in the compile output and use the
|
||||
appropriate replacement constant from `std::f32::consts`..."""
|
||||
appropriate replacement constant from `std::f32::consts`."""
|
||||
|
||||
[[exercises]]
|
||||
name = "clippy2"
|
||||
@ -1142,7 +1149,11 @@ test = false
|
||||
strict_clippy = true
|
||||
hint = """
|
||||
`for` loops over `Option` values are more clearly expressed as an `if-let`
|
||||
statement."""
|
||||
statement.
|
||||
|
||||
Not required to solve this exercise, but if you are interested in when iterating
|
||||
over `Option` can be useful, read the following section in the documentation:
|
||||
https://doc.rust-lang.org/std/option/#iterating-over-option"""
|
||||
|
||||
[[exercises]]
|
||||
name = "clippy3"
|
||||
@ -1190,7 +1201,8 @@ hint = """
|
||||
Is there an implementation of `TryFrom` in the standard library that can both do
|
||||
the required integer conversion and check the range of the input?
|
||||
|
||||
Challenge: Can you make the `TryFrom` implementations generic over many integer types?"""
|
||||
Challenge: Can you make the `TryFrom` implementations generic over many integer
|
||||
types?"""
|
||||
|
||||
[[exercises]]
|
||||
name = "as_ref_mut"
|
||||
|
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
let number = "T-H-R-E-E";
|
||||
println!("Spell a number: {}", number);
|
||||
println!("Spell a number: {number}");
|
||||
|
||||
// Using variable shadowing
|
||||
// https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing
|
||||
|
@ -1,4 +1,4 @@
|
||||
fn call_me(num: u32) {
|
||||
fn call_me(num: u8) {
|
||||
for i in 0..num {
|
||||
println!("Ring! Call number {}", i + 1);
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
fn bigger(a: i32, b: i32) -> i32 {
|
||||
if a > b {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
if a > b { a } else { b }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -1,10 +1,10 @@
|
||||
fn foo_if_fizz(fizzish: &str) -> &str {
|
||||
if fizzish == "fizz" {
|
||||
"foo"
|
||||
} else if fizzish == "fuzz" {
|
||||
"bar"
|
||||
fn picky_eater(food: &str) -> &str {
|
||||
if food == "strawberry" {
|
||||
"Yummy!"
|
||||
} else if food == "potato" {
|
||||
"I guess I can eat that."
|
||||
} else {
|
||||
"baz"
|
||||
"No thanks!"
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,17 +17,19 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn foo_for_fizz() {
|
||||
assert_eq!(foo_if_fizz("fizz"), "foo");
|
||||
fn yummy_food() {
|
||||
assert_eq!(picky_eater("strawberry"), "Yummy!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bar_for_fuzz() {
|
||||
assert_eq!(foo_if_fizz("fuzz"), "bar");
|
||||
fn neutral_food() {
|
||||
assert_eq!(picky_eater("potato"), "I guess I can eat that.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_to_baz() {
|
||||
assert_eq!(foo_if_fizz("literally anything"), "baz");
|
||||
fn default_disliked_food() {
|
||||
assert_eq!(picky_eater("broccoli"), "No thanks!");
|
||||
assert_eq!(picky_eater("gummy bears"), "No thanks!");
|
||||
assert_eq!(picky_eater("literally anything"), "No thanks!");
|
||||
}
|
||||
}
|
||||
|
@ -4,18 +4,16 @@ fn main() {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// TODO: Fix the compiler errors only by reordering the lines in the test.
|
||||
// Don't add, change or remove any line.
|
||||
#[test]
|
||||
fn move_semantics5() {
|
||||
let mut x = 100;
|
||||
fn move_semantics4() {
|
||||
let mut x = Vec::new();
|
||||
let y = &mut x;
|
||||
// `y` used here.
|
||||
*y += 100;
|
||||
y.push(42);
|
||||
// The mutable reference `y` is not used anymore,
|
||||
// therefore a new reference can be created.
|
||||
let z = &mut x;
|
||||
*z += 1000;
|
||||
assert_eq!(x, 1200);
|
||||
z.push(13);
|
||||
assert_eq!(x, [42, 13]);
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,5 @@
|
||||
#![allow(clippy::ptr_arg)]
|
||||
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
get_char(&data);
|
||||
|
||||
string_uppercase(data);
|
||||
}
|
||||
|
||||
// Borrows instead of taking ownership.
|
||||
// It is recommended to use `&str` instead of `&String` here. But this is
|
||||
// enough for now because we didn't handle strings yet.
|
||||
@ -21,3 +13,11 @@ fn string_uppercase(mut data: String) {
|
||||
|
||||
println!("{data}");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let data = "Rust is great!".to_string();
|
||||
|
||||
get_char(&data);
|
||||
|
||||
string_uppercase(data);
|
||||
}
|
||||
|
@ -1,14 +1,16 @@
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
Quit,
|
||||
Echo,
|
||||
Resize,
|
||||
Move,
|
||||
Echo,
|
||||
ChangeColor,
|
||||
Quit,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", Message::Quit);
|
||||
println!("{:?}", Message::Echo);
|
||||
println!("{:?}", Message::Resize);
|
||||
println!("{:?}", Message::Move);
|
||||
println!("{:?}", Message::Echo);
|
||||
println!("{:?}", Message::ChangeColor);
|
||||
println!("{:?}", Message::Quit);
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
Move { x: i64, y: i64 },
|
||||
Resize { width: u64, height: u64 },
|
||||
Move(Point),
|
||||
Echo(String),
|
||||
ChangeColor(u8, u8, u8),
|
||||
Quit,
|
||||
@ -15,7 +21,11 @@ impl Message {
|
||||
|
||||
fn main() {
|
||||
let messages = [
|
||||
Message::Move { x: 10, y: 30 },
|
||||
Message::Resize {
|
||||
width: 10,
|
||||
height: 30,
|
||||
},
|
||||
Message::Move(Point { x: 10, y: 15 }),
|
||||
Message::Echo(String::from("hello world")),
|
||||
Message::ChangeColor(200, 255, 255),
|
||||
Message::Quit,
|
||||
|
@ -1,44 +1,53 @@
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
enum Message {
|
||||
ChangeColor(u8, u8, u8),
|
||||
Echo(String),
|
||||
Resize { width: u64, height: u64 },
|
||||
Move(Point),
|
||||
Echo(String),
|
||||
ChangeColor(u8, u8, u8),
|
||||
Quit,
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x: u8,
|
||||
y: u8,
|
||||
}
|
||||
|
||||
struct State {
|
||||
color: (u8, u8, u8),
|
||||
width: u64,
|
||||
height: u64,
|
||||
position: Point,
|
||||
quit: bool,
|
||||
message: String,
|
||||
color: (u8, u8, u8),
|
||||
quit: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn change_color(&mut self, color: (u8, u8, u8)) {
|
||||
self.color = color;
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
self.quit = true;
|
||||
}
|
||||
|
||||
fn echo(&mut self, s: String) {
|
||||
self.message = s;
|
||||
fn resize(&mut self, width: u64, height: u64) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
}
|
||||
|
||||
fn move_position(&mut self, point: Point) {
|
||||
self.position = point;
|
||||
}
|
||||
|
||||
fn echo(&mut self, s: String) {
|
||||
self.message = s;
|
||||
}
|
||||
|
||||
fn change_color(&mut self, red: u8, green: u8, blue: u8) {
|
||||
self.color = (red, green, blue);
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
self.quit = true;
|
||||
}
|
||||
|
||||
fn process(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::ChangeColor(r, g, b) => self.change_color((r, g, b)),
|
||||
Message::Echo(s) => self.echo(s),
|
||||
Message::Resize { width, height } => self.resize(width, height),
|
||||
Message::Move(point) => self.move_position(point),
|
||||
Message::Echo(string) => self.echo(string),
|
||||
Message::ChangeColor(red, green, blue) => self.change_color(red, green, blue),
|
||||
Message::Quit => self.quit(),
|
||||
}
|
||||
}
|
||||
@ -55,21 +64,29 @@ mod tests {
|
||||
#[test]
|
||||
fn test_match_message_call() {
|
||||
let mut state = State {
|
||||
quit: false,
|
||||
width: 0,
|
||||
height: 0,
|
||||
position: Point { x: 0, y: 0 },
|
||||
color: (0, 0, 0),
|
||||
message: String::from("hello world"),
|
||||
color: (0, 0, 0),
|
||||
quit: false,
|
||||
};
|
||||
|
||||
state.process(Message::ChangeColor(255, 0, 255));
|
||||
state.process(Message::Echo(String::from("Hello world!")));
|
||||
state.process(Message::Resize {
|
||||
width: 10,
|
||||
height: 30,
|
||||
});
|
||||
state.process(Message::Move(Point { x: 10, y: 15 }));
|
||||
state.process(Message::Echo(String::from("Hello world!")));
|
||||
state.process(Message::ChangeColor(255, 0, 255));
|
||||
state.process(Message::Quit);
|
||||
|
||||
assert_eq!(state.color, (255, 0, 255));
|
||||
assert_eq!(state.width, 10);
|
||||
assert_eq!(state.height, 30);
|
||||
assert_eq!(state.position.x, 10);
|
||||
assert_eq!(state.position.y, 15);
|
||||
assert!(state.quit);
|
||||
assert_eq!(state.message, "Hello world!");
|
||||
assert_eq!(state.color, (255, 0, 255));
|
||||
assert!(state.quit);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ mod tests {
|
||||
assert_eq!(trim_me("Hello! "), "Hello!");
|
||||
assert_eq!(trim_me(" What's up!"), "What's up!");
|
||||
assert_eq!(trim_me(" Hola! "), "Hola!");
|
||||
assert_eq!(trim_me("Hi!"), "Hi!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,6 +1,7 @@
|
||||
fn string_slice(arg: &str) {
|
||||
println!("{arg}");
|
||||
}
|
||||
|
||||
fn string(arg: String) {
|
||||
println!("{arg}");
|
||||
}
|
||||
@ -17,12 +18,11 @@ fn main() {
|
||||
// Here, both answers work.
|
||||
// `.into()` converts a type into an expected type.
|
||||
// If it is called where `String` is expected, it will convert `&str` to `String`.
|
||||
// But if is called where `&str` is expected, then `&str` is kept `&str` since no
|
||||
// conversion is needed.
|
||||
string("nice weather".into());
|
||||
// But if it is called where `&str` is expected, then `&str` is kept `&str` since no conversion is needed.
|
||||
// If you remove the `#[allow(…)]` line, then Clippy will tell you to remove `.into()` below since it is a useless conversion.
|
||||
#[allow(clippy::useless_conversion)]
|
||||
string_slice("nice weather".into());
|
||||
// ^^^^^^^ the compiler recommends removing the `.into()`
|
||||
// call because it is a useless conversion.
|
||||
|
||||
string(format!("Interpolation {}", "Station"));
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
#[allow(dead_code)]
|
||||
mod delicious_snacks {
|
||||
// Added `pub` and used the expected alias after `as`.
|
||||
pub use self::fruits::PEAR as fruit;
|
||||
|
@ -5,7 +5,8 @@
|
||||
// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You
|
||||
// must add fruit to the basket so that there is at least one of each kind and
|
||||
// more than 11 in total - we have a lot of mouths to feed. You are not allowed
|
||||
// to insert any more of these fruits!
|
||||
// to insert any more of the fruits that are already in the basket (Apple,
|
||||
// Mango, and Lychee).
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -10,14 +10,14 @@ use std::collections::HashMap;
|
||||
|
||||
// A structure to store the goal details of a team.
|
||||
#[derive(Default)]
|
||||
struct Team {
|
||||
struct TeamScores {
|
||||
goals_scored: u8,
|
||||
goals_conceded: u8,
|
||||
}
|
||||
|
||||
fn build_scores_table(results: &str) -> HashMap<&str, Team> {
|
||||
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
|
||||
// The name of the team is the key and its associated struct is the value.
|
||||
let mut scores = HashMap::new();
|
||||
let mut scores = HashMap::<&str, TeamScores>::new();
|
||||
|
||||
for line in results.lines() {
|
||||
let mut split_iterator = line.split(',');
|
||||
@ -28,13 +28,13 @@ fn build_scores_table(results: &str) -> HashMap<&str, Team> {
|
||||
let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();
|
||||
|
||||
// Insert the default with zeros if a team doesn't exist yet.
|
||||
let team_1 = scores.entry(team_1_name).or_insert_with(Team::default);
|
||||
let team_1 = scores.entry(team_1_name).or_default();
|
||||
// Update the values.
|
||||
team_1.goals_scored += team_1_score;
|
||||
team_1.goals_conceded += team_2_score;
|
||||
|
||||
// Similarely for the second team.
|
||||
let team_2 = scores.entry(team_2_name).or_insert_with(Team::default);
|
||||
// Similarly for the second team.
|
||||
let team_2 = scores.entry(team_2_name).or_default();
|
||||
team_2.goals_scored += team_2_score;
|
||||
team_2.goals_conceded += team_1_score;
|
||||
}
|
||||
@ -60,9 +60,11 @@ England,Spain,1,0";
|
||||
fn build_scores() {
|
||||
let scores = build_scores_table(RESULTS);
|
||||
|
||||
assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"]
|
||||
assert!(
|
||||
["England", "France", "Germany", "Italy", "Poland", "Spain"]
|
||||
.into_iter()
|
||||
.all(|team_name| scores.contains_key(team_name)));
|
||||
.all(|team_name| scores.contains_key(team_name))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -10,7 +10,7 @@ fn main() {
|
||||
// Solution 1: Matching over the `Option` (not `&Option`) but without moving
|
||||
// out of the `Some` variant.
|
||||
match optional_point {
|
||||
Some(ref p) => println!("Co-ordinates are {},{}", p.x, p.y),
|
||||
Some(ref p) => println!("Coordinates are {},{}", p.x, p.y),
|
||||
// ^^^ added
|
||||
_ => panic!("No match!"),
|
||||
}
|
||||
@ -18,7 +18,8 @@ fn main() {
|
||||
// Solution 2: Matching over a reference (`&Option`) by added `&` before
|
||||
// `optional_point`.
|
||||
match &optional_point {
|
||||
Some(p) => println!("Co-ordinates are {},{}", p.x, p.y),
|
||||
//^ added
|
||||
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
|
||||
_ => panic!("No match!"),
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[allow(unused_variables, clippy::question_mark)]
|
||||
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||
let processing_fee = 1;
|
||||
let cost_per_item = 5;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![allow(clippy::comparison_chain)]
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
@ -11,12 +11,10 @@ struct PositiveNonzeroInteger(u64);
|
||||
|
||||
impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<Self, CreationError> {
|
||||
if value == 0 {
|
||||
Err(CreationError::Zero)
|
||||
} else if value < 0 {
|
||||
Err(CreationError::Negative)
|
||||
} else {
|
||||
Ok(Self(value as u64))
|
||||
match value.cmp(&0) {
|
||||
Ordering::Less => Err(CreationError::Negative),
|
||||
Ordering::Equal => Err(CreationError::Zero),
|
||||
Ordering::Greater => Ok(Self(value as u64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,26 @@ impl ParsePosNonzeroError {
|
||||
Self::Creation(err)
|
||||
}
|
||||
|
||||
fn from_parseint(err: ParseIntError) -> Self {
|
||||
fn from_parse_int(err: ParseIntError) -> Self {
|
||||
Self::ParseInt(err)
|
||||
}
|
||||
}
|
||||
|
||||
// As an alternative solution, implementing the `From` trait allows for the
|
||||
// automatic conversion from a `ParseIntError` into a `ParsePosNonzeroError`
|
||||
// using the `?` operator, without the need to call `map_err`.
|
||||
//
|
||||
// ```
|
||||
// let x: i64 = s.parse()?;
|
||||
// ```
|
||||
//
|
||||
// Traits like `From` will be dealt with in later exercises.
|
||||
impl From<ParseIntError> for ParsePosNonzeroError {
|
||||
fn from(err: ParseIntError) -> Self {
|
||||
ParsePosNonzeroError::ParseInt(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct PositiveNonzeroInteger(u64);
|
||||
|
||||
@ -44,7 +59,7 @@ impl PositiveNonzeroInteger {
|
||||
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
||||
// Return an appropriate error instead of panicking when `parse()`
|
||||
// returns an error.
|
||||
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?;
|
||||
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
trait Licensed {
|
||||
fn licensing_info(&self) -> String {
|
||||
"Default license".to_string()
|
||||
|
@ -5,11 +5,7 @@
|
||||
|
||||
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||
// ^^^^ ^^ ^^ ^^
|
||||
if x.len() > y.len() {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
if x.len() > y.len() { x } else { y }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -1,9 +1,5 @@
|
||||
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||
if x.len() > y.len() {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
}
|
||||
if x.len() > y.len() { x } else { y }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -9,7 +9,7 @@ impl Rectangle {
|
||||
if width <= 0 || height <= 0 {
|
||||
// Returning a `Result` would be better here. But we want to learn
|
||||
// how to test functions that can panic.
|
||||
panic!("Rectangle width and height can't be negative");
|
||||
panic!("Rectangle width and height must be positive");
|
||||
}
|
||||
|
||||
Rectangle { width, height }
|
||||
|
@ -1,6 +1,10 @@
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum DivisionError {
|
||||
// Example: 42 / 0
|
||||
DivideByZero,
|
||||
// Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`
|
||||
IntegerOverflow,
|
||||
// Example: 5 / 2 = 2.5
|
||||
NotDivisible,
|
||||
}
|
||||
|
||||
@ -9,6 +13,10 @@ fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
|
||||
return Err(DivisionError::DivideByZero);
|
||||
}
|
||||
|
||||
if a == i64::MIN && b == -1 {
|
||||
return Err(DivisionError::IntegerOverflow);
|
||||
}
|
||||
|
||||
if a % b != 0 {
|
||||
return Err(DivisionError::NotDivisible);
|
||||
}
|
||||
@ -51,6 +59,11 @@ mod tests {
|
||||
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integer_overflow() {
|
||||
assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_divisible() {
|
||||
assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));
|
||||
|
@ -25,6 +25,7 @@ fn factorial_fold(num: u64) -> u64 {
|
||||
// -> 1 * 2 is calculated, then the result 2 is multiplied by
|
||||
// the second element 3 so the result 6 is returned.
|
||||
// And so on…
|
||||
#[allow(clippy::unnecessary_fold)]
|
||||
(2..=num).fold(1, |acc, x| acc * x)
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,23 @@ fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Pr
|
||||
.sum()
|
||||
}
|
||||
|
||||
// Equivalent to `count_collection_iterator` and `count_iterator`, iterating as
|
||||
// if the collection was a single container instead of a container of containers
|
||||
// (and more accurately, a single iterator instead of an iterator of iterators).
|
||||
fn count_collection_iterator_flat(
|
||||
collection: &[HashMap<String, Progress>],
|
||||
value: Progress,
|
||||
) -> usize {
|
||||
// `collection` is a slice of hash maps.
|
||||
// collection = [{ "variables1": Complete, "from_str": None, … },
|
||||
// { "variables2": Complete, … }, … ]
|
||||
collection
|
||||
.iter()
|
||||
.flat_map(HashMap::values) // or just `.flatten()` when wanting the default iterator (`HashMap::iter`)
|
||||
.filter(|val| **val == value)
|
||||
.count()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// You can optionally experiment here.
|
||||
}
|
||||
@ -54,10 +71,9 @@ fn main() {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn get_map() -> HashMap<String, Progress> {
|
||||
use Progress::*;
|
||||
|
||||
fn get_map() -> HashMap<String, Progress> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(String::from("variables1"), Complete);
|
||||
map.insert(String::from("functions1"), Complete);
|
||||
@ -70,8 +86,6 @@ mod tests {
|
||||
}
|
||||
|
||||
fn get_vec_map() -> Vec<HashMap<String, Progress>> {
|
||||
use Progress::*;
|
||||
|
||||
let map = get_map();
|
||||
|
||||
let mut other = HashMap::new();
|
||||
@ -87,25 +101,25 @@ mod tests {
|
||||
#[test]
|
||||
fn count_complete() {
|
||||
let map = get_map();
|
||||
assert_eq!(count_iterator(&map, Progress::Complete), 3);
|
||||
assert_eq!(count_iterator(&map, Complete), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_some() {
|
||||
let map = get_map();
|
||||
assert_eq!(count_iterator(&map, Progress::Some), 1);
|
||||
assert_eq!(count_iterator(&map, Some), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_none() {
|
||||
let map = get_map();
|
||||
assert_eq!(count_iterator(&map, Progress::None), 2);
|
||||
assert_eq!(count_iterator(&map, None), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_complete_equals_for() {
|
||||
let map = get_map();
|
||||
let progress_states = [Progress::Complete, Progress::Some, Progress::None];
|
||||
let progress_states = [Complete, Some, None];
|
||||
for progress_state in progress_states {
|
||||
assert_eq!(
|
||||
count_for(&map, progress_state),
|
||||
@ -117,34 +131,38 @@ mod tests {
|
||||
#[test]
|
||||
fn count_collection_complete() {
|
||||
let collection = get_vec_map();
|
||||
assert_eq!(
|
||||
count_collection_iterator(&collection, Progress::Complete),
|
||||
6,
|
||||
);
|
||||
assert_eq!(count_collection_iterator(&collection, Complete), 6);
|
||||
assert_eq!(count_collection_iterator_flat(&collection, Complete), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_collection_some() {
|
||||
let collection = get_vec_map();
|
||||
assert_eq!(count_collection_iterator(&collection, Progress::Some), 1);
|
||||
assert_eq!(count_collection_iterator(&collection, Some), 1);
|
||||
assert_eq!(count_collection_iterator_flat(&collection, Some), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_collection_none() {
|
||||
let collection = get_vec_map();
|
||||
assert_eq!(count_collection_iterator(&collection, Progress::None), 4);
|
||||
assert_eq!(count_collection_iterator(&collection, None), 4);
|
||||
assert_eq!(count_collection_iterator_flat(&collection, None), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_collection_equals_for() {
|
||||
let collection = get_vec_map();
|
||||
let progress_states = [Progress::Complete, Progress::Some, Progress::None];
|
||||
let progress_states = [Complete, Some, None];
|
||||
|
||||
for progress_state in progress_states {
|
||||
assert_eq!(
|
||||
count_collection_for(&collection, progress_state),
|
||||
count_collection_iterator(&collection, progress_state),
|
||||
);
|
||||
assert_eq!(
|
||||
count_collection_for(&collection, progress_state),
|
||||
count_collection_iterator_flat(&collection, progress_state),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,9 @@ mod tests {
|
||||
#[test]
|
||||
fn owned_no_mutation() {
|
||||
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
|
||||
// case, no mutation occurs and thus also no clone. But the result is
|
||||
// still owned because it was never borrowed or mutated.
|
||||
// case, no mutation occurs (all numbers are already absolute) and thus
|
||||
// also no clone. But the result is still owned because it was never
|
||||
// borrowed or mutated.
|
||||
let vec = vec![0, 1, 2];
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
@ -56,9 +57,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn owned_mutation() {
|
||||
// Of course this is also the case if a mutation does occur. In this
|
||||
// case, the call to `to_mut()` in the `abs_all` function returns a
|
||||
// reference to the same data as before.
|
||||
// Of course this is also the case if a mutation does occur (not all
|
||||
// numbers are absolute). In this case, the call to `to_mut()` in the
|
||||
// `abs_all` function returns a reference to the same data as before.
|
||||
let vec = vec![-1, 0, 1];
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
|
@ -8,7 +8,6 @@ use std::rc::Rc;
|
||||
#[derive(Debug)]
|
||||
struct Sun;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
enum Planet {
|
||||
Mercury(Rc<Sun>),
|
||||
@ -64,12 +63,10 @@ mod tests {
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
|
||||
saturn.details();
|
||||
|
||||
// TODO
|
||||
let uranus = Planet::Uranus(Rc::clone(&sun));
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
|
||||
uranus.details();
|
||||
|
||||
// TODO
|
||||
let neptune = Planet::Neptune(Rc::clone(&sun));
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
|
||||
neptune.details();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// This program spawns multiple threads that each run for at least 250ms, and
|
||||
// each thread returns how much time they took to complete. The program should
|
||||
// This program spawns multiple threads that each runs for at least 250ms, and
|
||||
// each thread returns how much time it took to complete. The program should
|
||||
// wait until all the spawned threads have finished and should collect their
|
||||
// return values into a vector.
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::{sync::mpsc, thread, time::Duration};
|
||||
|
||||
struct Queue {
|
||||
length: u32,
|
||||
first_half: Vec<u32>,
|
||||
second_half: Vec<u32>,
|
||||
}
|
||||
@ -9,7 +8,6 @@ struct Queue {
|
||||
impl Queue {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
length: 10,
|
||||
first_half: vec![1, 2, 3, 4, 5],
|
||||
second_half: vec![6, 7, 8, 9, 10],
|
||||
}
|
||||
@ -50,17 +48,15 @@ mod tests {
|
||||
fn threads3() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let queue = Queue::new();
|
||||
let queue_length = queue.length;
|
||||
|
||||
send_tx(queue, tx);
|
||||
|
||||
let mut total_received: u32 = 0;
|
||||
for received in rx {
|
||||
println!("Got: {received}");
|
||||
total_received += 1;
|
||||
let mut received = Vec::with_capacity(10);
|
||||
for value in rx {
|
||||
received.push(value);
|
||||
}
|
||||
|
||||
println!("Number of received values: {total_received}");
|
||||
assert_eq!(total_received, queue_length);
|
||||
received.sort();
|
||||
assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,11 @@ use std::mem;
|
||||
#[rustfmt::skip]
|
||||
#[allow(unused_variables, unused_assignments)]
|
||||
fn main() {
|
||||
let my_option: Option<()> = None;
|
||||
let my_option: Option<&str> = None;
|
||||
// `unwrap` of an `Option` after checking if it is `None` will panic.
|
||||
// Use `if-let` instead.
|
||||
if let Some(value) = my_option {
|
||||
println!("{value:?}");
|
||||
println!("{value}");
|
||||
}
|
||||
|
||||
// A comma was missing.
|
||||
@ -15,7 +15,7 @@ fn main() {
|
||||
-1, -2, -3,
|
||||
-4, -5, -6,
|
||||
];
|
||||
println!("My array! Here it is: {:?}", my_arr);
|
||||
println!("My array! Here it is: {my_arr:?}");
|
||||
|
||||
let mut my_empty_vec = vec![1, 2, 3, 4, 5];
|
||||
// `resize` mutates a vector instead of returning a new one.
|
||||
@ -27,5 +27,5 @@ fn main() {
|
||||
let mut value_b = 66;
|
||||
// Use `mem::swap` to correctly swap two values.
|
||||
mem::swap(&mut value_a, &mut value_b);
|
||||
println!("value a: {}; value b: {}", value_a, value_b);
|
||||
println!("value a: {value_a}; value b: {value_b}");
|
||||
}
|
||||
|
@ -2,9 +2,10 @@
|
||||
// about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and
|
||||
// https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively.
|
||||
|
||||
// Obtain the number of bytes (not characters) in the given argument.
|
||||
// Obtain the number of bytes (not characters) in the given argument
|
||||
// (`.len()` returns the number of bytes in a string).
|
||||
fn byte_counter<T: AsRef<str>>(arg: T) -> usize {
|
||||
arg.as_ref().as_bytes().len()
|
||||
arg.as_ref().len()
|
||||
}
|
||||
|
||||
// Obtain the number of characters (not bytes) in the given argument.
|
||||
@ -15,7 +16,7 @@ fn char_counter<T: AsRef<str>>(arg: T) -> usize {
|
||||
// Squares a number using `as_mut()`.
|
||||
fn num_sq<T: AsMut<u32>>(arg: &mut T) {
|
||||
let arg = arg.as_mut();
|
||||
*arg = *arg * *arg;
|
||||
*arg *= *arg;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
6
solutions/README.md
Normal file
6
solutions/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Official Rustlings solutions
|
||||
|
||||
Before you finish an exercise, its solution file will only contain an empty `main` function.
|
||||
The content of this file will be automatically replaced by the actual solution once you finish the exercise.
|
||||
|
||||
Note that these solutions are often only _one possibility_ to solve an exercise.
|
@ -1,8 +1,7 @@
|
||||
// Mary is buying apples. The price of an apple is calculated as follows:
|
||||
// - An apple costs 2 rustbucks.
|
||||
// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck!
|
||||
// Write a function that calculates the price of an order of apples given the
|
||||
// quantity bought.
|
||||
// - However, if Mary buys more than 40 apples, the price of each apple in the
|
||||
// entire order is reduced to only 1 rustbuck!
|
||||
|
||||
fn calculate_price_of_apples(n_apples: u64) -> u64 {
|
||||
if n_apples > 40 {
|
||||
|
@ -1,10 +1,3 @@
|
||||
// This is a quiz for the following sections:
|
||||
// - Strings
|
||||
// - Vecs
|
||||
// - Move semantics
|
||||
// - Modules
|
||||
// - Enums
|
||||
//
|
||||
// Let's build a little machine in the form of a function. As input, we're going
|
||||
// to give a list of strings and commands. These commands determine what action
|
||||
// is going to be applied to the string. It can either be:
|
||||
@ -13,7 +6,7 @@
|
||||
// - Append "bar" to the string a specified amount of times
|
||||
//
|
||||
// The exact form of this will be:
|
||||
// - The input is going to be a vector of a 2-length tuple,
|
||||
// - The input is going to be a vector of 2-length tuples,
|
||||
// the first element is the string, the second one is the command.
|
||||
// - The output element is going to be a vector of strings.
|
||||
|
||||
@ -31,17 +24,12 @@ mod my_module {
|
||||
pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {
|
||||
let mut output = Vec::new();
|
||||
|
||||
for (mut string, command) in input {
|
||||
for (string, command) in input {
|
||||
// Create the new string.
|
||||
let new_string = match command {
|
||||
Command::Uppercase => string.to_uppercase(),
|
||||
Command::Trim => string.trim().to_string(),
|
||||
Command::Append(n) => {
|
||||
for _ in 0..n {
|
||||
string += "bar";
|
||||
}
|
||||
string
|
||||
}
|
||||
Command::Append(n) => string + &"bar".repeat(n),
|
||||
};
|
||||
|
||||
// Push the new string to the output vector.
|
||||
@ -56,15 +44,10 @@ mod my_module {
|
||||
pub fn transformer_iter(input: Vec<(String, Command)>) -> Vec<String> {
|
||||
input
|
||||
.into_iter()
|
||||
.map(|(mut string, command)| match command {
|
||||
.map(|(string, command)| match command {
|
||||
Command::Uppercase => string.to_uppercase(),
|
||||
Command::Trim => string.trim().to_string(),
|
||||
Command::Append(n) => {
|
||||
for _ in 0..n {
|
||||
string += "bar";
|
||||
}
|
||||
string
|
||||
}
|
||||
Command::Append(n) => string + &"bar".repeat(n),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -79,8 +62,8 @@ mod tests {
|
||||
// Import `transformer`.
|
||||
use super::my_module::transformer;
|
||||
|
||||
use super::my_module::transformer_iter;
|
||||
use super::Command;
|
||||
use super::my_module::transformer_iter;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
|
@ -1,7 +1,3 @@
|
||||
// This quiz tests:
|
||||
// - Generics
|
||||
// - Traits
|
||||
//
|
||||
// An imaginary magical school has a new report card generation system written
|
||||
// in Rust! Currently, the system only supports creating report cards where the
|
||||
// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user