Compare commits

..

No commits in common. "main" and "v6.0.1" have entirely different histories.
main ... v6.0.1

169 changed files with 2969 additions and 4499 deletions

View File

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

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
root = true
[*.rs]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4

View File

@ -1,16 +1,10 @@
name: Check
name: Rustlings Tests
on:
push:
branches: [main]
paths-ignore:
- website
- '*.md'
pull_request:
branches: [main]
paths-ignore:
- website
- '*.md'
env:
CARGO_TERM_COLOR: always
@ -20,28 +14,30 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Clippy
run: cargo clippy -- --deny warnings
- run: cargo clippy -- --deny warnings
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: rustfmt
run: cargo fmt --all --check
- uses: DavidAnson/markdownlint-cli2-action@v16
with:
globs: "exercises/**/*.md"
- name: Run cargo fmt
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: cargo test
run: cargo test --workspace
- name: Run cargo test
run: cargo test
dev-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: swatinem/rust-cache@v2
- name: rustlings dev check
run: cargo dev check --require-solutions
- name: Run rustlings dev check
run: cargo run -- dev check --require-solutions

87
.github/workflows/web.yml vendored Normal file
View File

@ -0,0 +1,87 @@
# 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

View File

@ -1,43 +0,0 @@
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
View File

@ -6,6 +6,10 @@ Cargo.lock
# State file
.rustlings-state.txt
# oranda
public/
.netlify
# OS
.DS_Store
.direnv/

2
.markdownlint.yml Normal file
View File

@ -0,0 +1,2 @@
# MD013/line-length Line length, Expected: 80
MD013: false

View File

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

View File

@ -1,134 +1,12 @@
## Unreleased
### Changed
- `vecs2`: Removed the use of `map` and `collect`, which are only taught later.
## 6.5.0 (2025-08-21)
### Added
- Check that Clippy is installed before initialization
### Changed
- Upgrade to Rust edition 2024
- Raise the minimum supported Rust version to `1.88`
- Don't follow symlinks in the file watcher
- `dev new`: Don't add `.rustlings-state.txt` to `.gitignore`
### Fixed
- Fix file links in VS Code
- Fix error printing when the progress bar is shown
- `dev check`: Don't check formatting if there are no solution files
## 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.
<a name="6.0.1"></a>
## 6.0.1 (2024-07-04)
Small exercise improvements and fixes.
Most importantly, fixed that the exercise `clippy1` was already solved 😅
<a name="6.0.0"></a>
## 6.0.0 (2024-07-03)
This release is the result of a complete rewrite to deliver a ton of new features and improvements ✨
@ -157,7 +35,7 @@ You can read about the motivations of this change in [this issue](https://github
### List mode
A new list mode was added!
A list mode was added using [Ratatui](https://ratatui.rs).
You can enter it by entering `l` in the watch mode.
It offers the following features:
@ -186,13 +64,15 @@ 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 🥰
### Community Exercises
### Third party exercises
Rustlings now supports community exercises!
Rustlings now supports 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 [community exercises](https://rustlings.rust-lang.org/community-exercises)!
Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)!
<a name="5.6.1"></a>
## 5.6.1 (2023-09-18)
@ -209,6 +89,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- `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
@ -248,12 +130,16 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -288,6 +174,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -303,6 +191,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- `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
@ -331,6 +221,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- Bumped min Rust version to 1.58 in installation script
<a name="5.3.0"></a>
## 5.3.0 (2022-12-23)
#### Added
@ -363,6 +255,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -376,6 +270,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- Fixed a typo in README.md
<a name="5.2.0"></a>
## 5.2.0 (2022-08-27)
#### Added
@ -392,12 +288,16 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -432,6 +332,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -504,6 +406,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -524,6 +428,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -544,6 +450,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -584,6 +492,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -606,6 +516,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -626,6 +538,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -667,6 +581,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -689,6 +605,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -709,6 +627,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -731,6 +651,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -772,6 +694,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -794,6 +718,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -804,11 +730,13 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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 compatible with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401))
- Update deps to version compatable 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))
@ -831,6 +759,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -848,6 +778,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -868,6 +800,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -879,6 +813,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -903,6 +839,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -911,6 +849,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -927,6 +867,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- **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
@ -942,12 +884,16 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -955,6 +901,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -962,6 +910,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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
@ -974,6 +924,8 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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)
@ -983,12 +935,16 @@ Then follow the link to the guide about [community exercises](https://rustlings.
- 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.

843
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,27 @@
[workspace]
resolver = "2"
exclude = [
"tests/test_exercises",
"tests/fixture/failure",
"tests/fixture/state",
"tests/fixture/success",
"dev",
]
[workspace.package]
version = "6.5.0"
version = "6.0.1"
authors = [
"Mo Bitar <mo8it@proton.me>", # https://github.com/mo8it
"Liv <mokou@fastmail.com>", # https://github.com/shadows-withal
"Liv <mokou@fastmail.com>",
"Mo Bitar <mo8it@proton.me>",
# Alumni
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>", # https://github.com/carols10cents
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>",
]
repository = "https://github.com/rust-lang/rustlings"
license = "MIT"
edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and `CARGO_TOML` in `dev new`.
rust-version = "1.88"
edition = "2021"
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
toml = { version = "0.9", default-features = false, features = ["std", "parse", "serde"] }
serde = { version = "1.0.203", features = ["derive"] }
toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] }
[package]
name = "rustlings"
@ -29,7 +31,6 @@ authors.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
rust-version.workspace = true
keywords = [
"exercise",
"learning",
@ -45,20 +46,21 @@ include = [
]
[dependencies]
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.5.0" }
serde_json = "1.0"
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"
serde.workspace = true
toml.workspace = true
[target.'cfg(not(windows))'.dependencies]
rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] }
toml_edit.workspace = true
[dev-dependencies]
tempfile = "3.21"
assert_cmd = "2.0.14"
predicates = "3.1.0"
[profile.release]
panic = "abort"
@ -68,20 +70,3 @@ 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
View File

@ -1,7 +1,143 @@
# [Rustlings](https://rustlings.rust-lang.org) 🦀
<div class="oranda-hide">
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) 📚_
# Rustlings 🦀❤️
Visit the **website** for a demo, info about setup and more:
</div>
## ➡️ [rustlings.rust-lang.org](https://rustlings.rust-lang.org) ⬅️
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) 🎉

53
THIRD_PARTY_EXERCISES.md Normal file
View File

@ -0,0 +1,53 @@
# 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 😃

View File

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

View File

@ -1,11 +0,0 @@
disallowed-types = [
{ path = "crossterm::style::Stylize", reason = "inefficient, use `.queue(…)` instead" },
{ path = "crossterm::style::styled_content::StyledContent", reason = "inefficient, use `.queue(…)` instead" },
]
disallowed-methods = [
{ path = "crossterm::style::style", reason = "inefficient, use `.queue(…)` instead" },
{ path = "std::thread::spawn", replacement = "std::thread::Builder::spawn", reason = "handle the error" },
{ path = "std::thread::Scope::spawn", replacement = "std::thread::Builder::spawn", reason = "handle the error" },
{ path = "std::process::exit", replacement = "std::process::ExitCode" },
]

View File

@ -1,4 +1,4 @@
# Don't edit the `bin` list manually! It is updated by `cargo dev update`. This comment line will be stripped in `rustlings init`.
# Don't edit the `bin` list manually! It is updated by `cargo run -- 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,32 +192,6 @@ bin = [
[package]
name = "exercises"
edition = "2024"
edition = "2021"
# 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"

View File

@ -1 +1 @@
This file is used to check if the user tries to run Rustlings in the repository (the method before version 6)
This file is used to check if the user tries to run Rustlings in the repository (the method before v6)

View File

@ -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,7 +6,8 @@
// Try adding a new `println!` and check the updated output in the terminal.
fn main() {
println!(r#" Welcome to... "#);
println!("Hello and");
println!(r#" welcome to... "#);
println!(r#" _ _ _ "#);
println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);

View File

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

View File

@ -1,5 +1,5 @@
fn main() {
// TODO: Add the missing keyword.
// TODO: Add missing keyword.
x = 5;
println!("x has the value {x}");

View File

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

View File

@ -1,4 +1,4 @@
fn call_me(num: u8) {
fn call_me(num: u32) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
}

View File

@ -1,7 +1,7 @@
// TODO: Fix the compiler error on this function.
fn picky_eater(food: &str) -> &str {
if food == "strawberry" {
"Yummy!"
fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else {
1
}
@ -18,20 +18,18 @@ mod tests {
use super::*;
#[test]
fn yummy_food() {
// This means that calling `picky_eater` with the argument "strawberry" should return "Yummy!".
assert_eq!(picky_eater("strawberry"), "Yummy!");
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");
}
#[test]
fn neutral_food() {
assert_eq!(picky_eater("potato"), "I guess I can eat that.");
fn bar_for_fuzz() {
assert_eq!(foo_if_fizz("fuzz"), "bar");
}
#[test]
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!");
fn default_to_baz() {
assert_eq!(foo_if_fizz("literally anything"), "baz");
}
}

View File

@ -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/book/ch03-02-data-types.html)
- [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html)
- [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)

View File

@ -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/book/ch08-01-vectors.html)
- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/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)

View File

@ -9,6 +9,26 @@ fn vec_loop(input: &[i32]) -> Vec<i32> {
output
}
fn vec_map_example(input: &[i32]) -> Vec<i32> {
// An example of collecting a vector after mapping.
// We map each element of the `input` slice to its value plus 1.
// If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`.
input.iter().map(|element| element + 1).collect()
}
fn vec_map(input: &[i32]) -> Vec<i32> {
// TODO: Here, we also want to multiply each element in the `input` slice
// by 2, but with iterator mapping instead of manually pushing into an empty
// vector.
// See the example in the function `vec_map_example` above.
input
.iter()
.map(|element| {
// ???
})
.collect()
}
fn main() {
// You can optionally experiment here.
}
@ -23,4 +43,18 @@ mod tests {
let ans = vec_loop(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
#[test]
fn test_vec_map_example() {
let input = [1, 2, 3];
let ans = vec_map_example(&input);
assert_eq!(ans, [2, 3, 4]);
}
#[test]
fn test_vec_map() {
let input = [2, 4, 6, 8, 10];
let ans = vec_map(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
}

View File

@ -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_semantics4() {
let mut x = Vec::new();
fn move_semantics5() {
let mut x = 100;
let y = &mut x;
let z = &mut x;
y.push(42);
z.push(13);
assert_eq!(x, [42, 13]);
*y += 100;
*z += 1000;
assert_eq!(x, 1200);
}
}

View File

@ -3,6 +3,14 @@
// 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()
@ -14,11 +22,3 @@ fn string_uppercase(mut data: &String) {
println!("{data}");
}
fn main() {
let data = "Rust is great!".to_string();
get_char(data);
string_uppercase(&data);
}

View File

@ -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 {

View File

@ -1,10 +1,10 @@
# Enums
Rust allows you to define types called "enums" which enumerate possible values.
Enums are a feature in many languages, but their capabilities differ in each language. 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. Rusts 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/ch19-03-pattern-syntax.html)
- [Pattern syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html)

View File

@ -4,9 +4,8 @@ enum Message {
}
fn main() {
println!("{:?}", Message::Resize);
println!("{:?}", Message::Move);
println!("{:?}", Message::Echo);
println!("{:?}", Message::ChangeColor);
println!("{:?}", Message::Quit);
println!("{:?}", Message::Echo);
println!("{:?}", Message::Move);
println!("{:?}", Message::ChangeColor);
}

View File

@ -1,9 +1,4 @@
#[derive(Debug)]
struct Point {
x: u64,
y: u64,
}
#[allow(dead_code)]
#[derive(Debug)]
enum Message {
// TODO: Define the different variants used below.
@ -17,11 +12,7 @@ impl Message {
fn main() {
let messages = [
Message::Resize {
width: 10,
height: 30,
},
Message::Move(Point { x: 10, y: 15 }),
Message::Move { x: 10, y: 30 },
Message::Echo(String::from("hello world")),
Message::ChangeColor(200, 255, 255),
Message::Quit,

View File

@ -1,51 +1,40 @@
struct Point {
x: u64,
y: u64,
enum Message {
// TODO: Implement the message variant types based on their usage below.
}
enum Message {
Resize { width: u64, height: u64 },
Move(Point),
Echo(String),
ChangeColor(u8, u8, u8),
Quit,
struct Point {
x: u8,
y: u8,
}
struct State {
width: u64,
height: u64,
position: Point,
message: String,
// RGB color composed of red, green and blue.
color: (u8, u8, u8),
position: Point,
quit: bool,
message: String,
}
impl State {
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 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 move_position(&mut self, point: Point) {
self.position = point;
}
fn process(&mut self, message: Message) {
// TODO: Create a match expression to process the different message
// variants using the methods defined above.
// 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))`
}
}
@ -60,29 +49,21 @@ mod tests {
#[test]
fn test_match_message_call() {
let mut state = State {
width: 0,
height: 0,
position: Point { x: 0, y: 0 },
message: String::from("hello world"),
color: (0, 0, 0),
quit: false,
position: Point { x: 0, y: 0 },
color: (0, 0, 0),
message: 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::Echo(String::from("Hello world!")));
state.process(Message::Move(Point { x: 10, y: 15 }));
state.process(Message::Quit);
assert_eq!(state.width, 10);
assert_eq!(state.height, 30);
assert_eq!(state.color, (255, 0, 255));
assert_eq!(state.position.x, 10);
assert_eq!(state.position.y, 15);
assert_eq!(state.message, "Hello world!");
assert_eq!(state.color, (255, 0, 255));
assert!(state.quit);
assert_eq!(state.message, "Hello world!");
}
}

View File

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

View File

@ -4,7 +4,6 @@ fn placeholder() {}
fn string_slice(arg: &str) {
println!("{arg}");
}
fn string(arg: String) {
println!("{arg}");
}

View File

@ -1,6 +1,7 @@
// You can bring module paths into scopes and provide new names for them with
// the `use` and `as` keywords.
#[allow(dead_code)]
mod delicious_snacks {
// TODO: Add the following two `use` statements after fixing them.
// use self::fruits::PEAR as ???;

View File

@ -5,8 +5,7 @@
// 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 the fruits that are already in the basket (Apple,
// Mango, and Lychee).
// to insert any more of these fruits!
use std::collections::HashMap;

View File

@ -10,14 +10,14 @@ use std::collections::HashMap;
// A structure to store the goal details of a team.
#[derive(Default)]
struct TeamScores {
struct Team {
goals_scored: u8,
goals_conceded: u8,
}
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
fn build_scores_table(results: &str) -> HashMap<&str, Team> {
// The name of the team is the key and its associated struct is the value.
let mut scores = HashMap::<&str, TeamScores>::new();
let mut scores = HashMap::new();
for line in results.lines() {
let mut split_iterator = line.split(',');

View File

@ -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/book/ch10-01-syntax.html#in-enum-definitions)
- [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions)
- [Option Module Documentation](https://doc.rust-lang.org/std/option/)
- [Option 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)

View File

@ -1,8 +1,8 @@
// This function returns how much ice cream there is left in the fridge.
// This function returns how much icecream there is left in the fridge.
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
// someone eats it all, so no ice cream is left (value 0). Return `None` if
// someone eats it all, so no icecream is left (value 0). Return `None` if
// `hour_of_day` is higher than 23.
fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {
fn maybe_icecream(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 ice_creams = maybe_ice_cream(12);
let icecreams = maybe_icecream(12);
assert_eq!(ice_creams, 5); // Don't change this line.
assert_eq!(icecreams, 5); // Don't change this line.
}
#[test]
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);
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);
}
}

View File

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

View File

@ -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 arent serious enough to require the program to stop entirely.
Sometimes, when a function fails, its for a reason that you can easily interpret and respond to.
For example, if you try to open a file and that operation fails because the file doesnt exist, you might want to create the file instead of terminating the process.
## Further information

View File

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

View File

@ -19,7 +19,6 @@ 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 {

View File

@ -1,3 +1,5 @@
#![allow(clippy::comparison_chain)]
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
@ -10,7 +12,6 @@ struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
// TODO: This function shouldn't always return an `Ok`.
// Read the tests below to clarify what should be returned.
Ok(Self(value as u64))
}
}

View File

@ -6,7 +6,7 @@
//
// In short, this particular use case for boxes is for when you want to own a
// value and you care only that it is a type which implements a particular
// trait. To do so, the `Box` is declared as of type `Box<dyn Trait>` where
// trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where
// `Trait` is the trait the compiler looks for on any value used in that
// context. For this exercise, that context is the potential errors which
// can be returned in a `Result`.
@ -39,8 +39,8 @@ struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
0 => Err(CreationError::Zero),
x if x < 0 => Err(CreationError::Negative),
x => Ok(PositiveNonzeroInteger(x as u64)),
}
}

View File

@ -25,7 +25,7 @@ impl ParsePosNonzeroError {
}
// TODO: Add another error conversion function here.
// fn from_parse_int(???) -> Self { ??? }
// fn from_parseint(???) -> Self { ??? }
}
#[derive(PartialEq, Debug)]

View File

@ -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/book/ch10-01-syntax.html)
- [Generic Data Types](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html)
- [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html)

View File

@ -1,3 +1,5 @@
#![allow(dead_code)]
trait Licensed {
// TODO: Add a default implementation for `licensing_info` so that
// implementors like the two structs below can share that default behavior

View File

@ -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 must be positive");
panic!("Rectangle width and height can't be negative");
}
Rectangle { width, height }

View File

@ -1,16 +1,12 @@
#[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: i64, b: i64) -> Result<i64, DivisionError> {
fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
todo!();
}
@ -39,8 +35,6 @@ mod tests {
#[test]
fn test_success() {
assert_eq!(divide(81, 9), Ok(9));
assert_eq!(divide(81, -1), Ok(-81));
assert_eq!(divide(i64::MIN, i64::MIN), Ok(1));
}
#[test]
@ -48,11 +42,6 @@ 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));

View File

@ -1,8 +1,5 @@
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
//
fn factorial(num: u8) -> u64 {
// TODO: Complete this function to return the factorial of `num`.
// Do not use:
// - early returns (using the `return` keyword explicitly)
// Try not to use:

View File

@ -45,9 +45,8 @@ mod tests {
#[test]
fn owned_no_mutation() {
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
// 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.
// case, no mutation occurs 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);
@ -57,9 +56,9 @@ mod tests {
#[test]
fn owned_mutation() {
// 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.
// 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.
let vec = vec![-1, 0, 1];
let mut input = Cow::from(vec);
abs_all(&mut input);

View File

@ -8,6 +8,7 @@ use std::rc::Rc;
#[derive(Debug)]
struct Sun;
#[allow(dead_code)]
#[derive(Debug)]
enum Planet {
Mercury(Rc<Sun>),

View File

@ -1,5 +1,5 @@
// 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
// 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
// wait until all the spawned threads have finished and should collect their
// return values into a vector.

View File

@ -1,6 +1,7 @@
use std::{sync::mpsc, thread, time::Duration};
struct Queue {
length: u32,
first_half: Vec<u32>,
second_half: Vec<u32>,
}
@ -8,6 +9,7 @@ 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],
}
@ -46,15 +48,17 @@ mod tests {
fn threads3() {
let (tx, rx) = mpsc::channel();
let queue = Queue::new();
let queue_length = queue.length;
send_tx(queue, tx);
let mut received = Vec::with_capacity(10);
for value in rx {
received.push(value);
let mut total_received: u32 = 0;
for received in rx {
println!("Got: {received}");
total_received += 1;
}
received.sort();
assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
println!("Number of received values: {total_received}");
assert_eq!(total_received, queue_length);
}
}

View File

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

View File

@ -1,25 +1,22 @@
// Here are some more easy Clippy fixes so you can see its utility.
// Here are some more easy Clippy fixes so you can see its utility 📎
// TODO: Fix all the Clippy lints.
#[rustfmt::skip]
#[allow(unused_variables, unused_assignments)]
fn main() {
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.
let my_option: Option<()> = None;
if my_option.is_none() {
println!("{}", my_option.unwrap());
println!("{:?}", my_option.unwrap());
}
#[rustfmt::skip]
let my_arr = &[
-1, -2, -3
-4, -5, -6
];
println!("My array! Here it is: {my_arr:?}");
let mut my_vec = vec![1, 2, 3, 4, 5];
my_vec.resize(0, 5);
println!("This Vec is empty, see? {my_vec:?}");
let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
println!("This Vec is empty, see? {my_empty_vec:?}");
let mut value_a = 45;
let mut value_b = 66;

View File

@ -2,11 +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
// (`.len()` returns the number of bytes in a string).
// Obtain the number of bytes (not characters) in the given argument.
// TODO: Add the `AsRef` trait appropriately as a trait bound.
fn byte_counter<T>(arg: T) -> usize {
arg.as_ref().len()
arg.as_ref().as_bytes().len()
}
// Obtain the number of characters (not bytes) in the given argument.

View File

@ -25,7 +25,7 @@ enum ParsePersonError {
ParseInt(ParseIntError),
}
// TODO: Complete this `FromStr` implementation to be able to parse a `Person`
// TODO: Complete this `From` 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>()`.

View File

@ -9,7 +9,7 @@
| vecs | §8.1 |
| move_semantics | §4.1-2 |
| structs | §5.1, §5.3 |
| enums | §6, §19.3 |
| enums | §6, §18.3 |
| strings | §8.2 |
| modules | §7 |
| hashmaps | §8.3 |
@ -22,6 +22,6 @@
| iterators | §13.2-4 |
| smart_pointers | §15, §16.3 |
| threads | §16.1-3 |
| macros | §20.5 |
| clippy | Appendix D |
| macros | §19.5 |
| clippy | §21.4 |
| conversions | n/a |

View File

@ -5,12 +5,12 @@
//
// Mary is buying apples. The price of an apple is calculated as follows:
// - An apple costs 2 rustbucks.
// - However, if Mary buys more than 40 apples, the price of each apple in the
// entire order is reduced to only 1 rustbuck!
// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck!
// TODO: Write a function that calculates the price of an order of apples given
// the quantity bought.
// fn calculate_price_of_apples(???) -> ??? { ??? }
// Put your function here!
// fn calculate_price_of_apples(???) -> ??? {
fn main() {
// You can optionally experiment here.

View File

@ -26,7 +26,7 @@ enum Command {
mod my_module {
use super::Command;
// TODO: Complete the function as described above.
// TODO: Complete the function.
// pub fn transformer(input: ???) -> ??? { ??? }
}

13
oranda.json Normal file
View File

@ -0,0 +1,13 @@
{
"project": {
"homepage": "https://rustlings.cool",
"repository": "https://github.com/rust-lang/rustlings"
},
"marketing": {
"analytics": {
"plausible": {
"domain": "rustlings.cool"
}
}
}
}

View File

@ -3,14 +3,7 @@
# Error out if any command fails
set -e
cargo run -- dev check
typos
cargo upgrades
# Similar to CI
cargo clippy -- --deny warnings
cargo fmt --all --check
cargo test --workspace
cargo dev check --require-solutions
# MSRV
cargo +1.88 dev check --require-solutions
cargo outdated -w --exit-code 1
cargo test --workspace --all-targets

View File

@ -6,7 +6,6 @@ authors.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
rust-version.workspace = true
include = [
"/src/",
"/info.toml",
@ -16,9 +15,6 @@ include = [
proc-macro = true
[dependencies]
quote = "1.0"
quote = "1.0.36"
serde.workspace = true
toml.workspace = true
[lints]
workspace = true
toml_edit.workspace = true

View File

@ -1,7 +1,6 @@
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:
@ -11,16 +10,15 @@ 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!
@ -33,7 +31,6 @@ 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`."""
@ -122,10 +119,10 @@ dir = "01_variables"
test = false
hint = """
We know about variables and mutability, but there is another important type of
variable available: constants.
variables 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.
@ -255,7 +252,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?"; 100];
let array = ["Are we there yet?"; 10];
```
Bonus: what are some other things you could have that would return `true`
@ -311,14 +308,22 @@ 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/book/ch08-01-vectors.html
Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html
of the Rust book to learn more."""
[[exercises]]
name = "vecs2"
dir = "05_vecs"
hint = """
Use the `.push()` method on the vector to push new elements to it."""
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.
After you've completed both functions, decide for yourself which approach you
like better.
What do you think is the more commonly used pattern under Rust developers?"""
# MOVE SEMANTICS
@ -326,8 +331,8 @@ Use the `.push()` method on the vector to push new elements to it."""
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).
@ -345,7 +350,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. This is called cloning in Rust."""
pass it to `fill_vec` instead."""
[[exercises]]
name = "move_semantics3"
@ -363,8 +368,7 @@ 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]]
@ -373,7 +377,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/book/ch04-02-references-and-borrowing.html
https://doc.rust-lang.org/stable/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
@ -411,7 +415,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/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
[[exercises]]
name = "structs3"
@ -440,7 +444,7 @@ dir = "08_enums"
test = false
hint = """
You can create enumerations that have different variants with different types
such as anonymous structs, structs, a single string, tuples, no data, etc."""
such as no data, anonymous structs, a single string, tuples, etc."""
[[exercises]]
name = "enums3"
@ -482,7 +486,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/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods"""
https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods"""
[[exercises]]
name = "strings3"
@ -493,18 +497,14 @@ 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.
For the `replace_me` method, you can check out the `replace` method:
https://doc.rust-lang.org/std/string/struct.String.html#method.replace"""
the string slice into an owned string, which you can then freely extend."""
[[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,14 +560,18 @@ hint = """
Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this.
Learn more in The Book:
https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value"""
https://doc.rust-lang.org/stable/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_default()` methods of `HashMap` to insert the
default value of `TeamScores` if a team doesn't exist in the table yet.
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 2: If there is already an entry for a given key, the value returned by
`entry()` can be updated based on the existing value.
@ -580,7 +584,7 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-
[[exercises]]
name = "quiz2"
dir = "quizzes"
hint = "The `+` operator can concatenate a `String` with a `&str`."
hint = "No hints this time ;)"
# OPTIONS
@ -734,7 +738,7 @@ name = "generics2"
dir = "14_generics"
hint = """
Related section in The Book:
https://doc.rust-lang.org/book/ch10-01-syntax.html#in-method-definitions"""
https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions"""
# TRAITS
@ -743,9 +747,7 @@ name = "traits1"
dir = "15_traits"
hint = """
More about traits in The Book:
https://doc.rust-lang.org/book/ch10-02-traits.html
The `+` operator can concatenate a `String` with a `&str`."""
https://doc.rust-lang.org/book/ch10-02-traits.html"""
[[exercises]]
name = "traits2"
@ -755,7 +757,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
value is owned anyway."""
the value is owned anyway."""
[[exercises]]
name = "traits3"
@ -866,7 +868,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/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
# STANDARD LIBRARY TYPES
@ -885,9 +887,9 @@ hint = """
`capitalize_first`:
The variable `first` is a `char`. It needs to be capitalized and added to the
remaining characters in `chars` in order to return the correct `String`.
remaining characters in `c` in order to return the correct `String`.
The remaining characters in `chars` can be viewed as a string slice using the
The remaining characters in `c` can be viewed as a string slice using the
`as_str` method.
The documentation for `char` contains many useful methods.
@ -1002,7 +1004,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/book/ch16-00-concurrency.html"""
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html"""
[[exercises]]
name = "cow1"
@ -1131,7 +1133,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"
@ -1140,11 +1142,7 @@ test = false
strict_clippy = true
hint = """
`for` loops over `Option` values are more clearly expressed as an `if-let`
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"""
statement."""
[[exercises]]
name = "clippy3"
@ -1192,8 +1190,7 @@ 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"

View File

@ -16,7 +16,7 @@ struct InfoFile {
#[proc_macro]
pub fn include_files(_: TokenStream) -> TokenStream {
let info_file = include_str!("../info.toml");
let exercises = toml::de::from_str::<InfoFile>(info_file)
let exercises = toml_edit::de::from_str::<InfoFile>(info_file)
.expect("Failed to parse `info.toml`")
.exercises;

View File

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

View File

@ -1,4 +1,4 @@
fn call_me(num: u8) {
fn call_me(num: u32) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
}

View File

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

View File

@ -1,10 +1,10 @@
fn picky_eater(food: &str) -> &str {
if food == "strawberry" {
"Yummy!"
} else if food == "potato" {
"I guess I can eat that."
fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else if fizzish == "fuzz" {
"bar"
} else {
"No thanks!"
"baz"
}
}
@ -17,19 +17,17 @@ mod tests {
use super::*;
#[test]
fn yummy_food() {
assert_eq!(picky_eater("strawberry"), "Yummy!");
fn foo_for_fizz() {
assert_eq!(foo_if_fizz("fizz"), "foo");
}
#[test]
fn neutral_food() {
assert_eq!(picky_eater("potato"), "I guess I can eat that.");
fn bar_for_fuzz() {
assert_eq!(foo_if_fizz("fuzz"), "bar");
}
#[test]
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!");
fn default_to_baz() {
assert_eq!(foo_if_fizz("literally anything"), "baz");
}
}

View File

@ -8,6 +8,22 @@ fn vec_loop(input: &[i32]) -> Vec<i32> {
output
}
fn vec_map_example(input: &[i32]) -> Vec<i32> {
// An example of collecting a vector after mapping.
// We map each element of the `input` slice to its value plus 1.
// If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`.
input.iter().map(|element| element + 1).collect()
}
fn vec_map(input: &[i32]) -> Vec<i32> {
// We will dive deeper into iterators, but for now, this is all what you
// had to do!
// Advanced note: This method is more efficient because it automatically
// preallocates enough capacity. This can be done manually in `vec_loop`
// using `Vec::with_capacity(input.len())` instead of `Vec::new()`.
input.iter().map(|element| 2 * element).collect()
}
fn main() {
// You can optionally experiment here.
}
@ -22,4 +38,18 @@ mod tests {
let ans = vec_loop(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
#[test]
fn test_vec_map_example() {
let input = [1, 2, 3];
let ans = vec_map_example(&input);
assert_eq!(ans, [2, 3, 4]);
}
#[test]
fn test_vec_map() {
let input = [2, 4, 6, 8, 10];
let ans = vec_map(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
}

View File

@ -4,16 +4,18 @@ fn main() {
#[cfg(test)]
mod tests {
// TODO: Fix the compiler errors only by reordering the lines in the test.
// Don't add, change or remove any line.
#[test]
fn move_semantics4() {
let mut x = Vec::new();
fn move_semantics5() {
let mut x = 100;
let y = &mut x;
// `y` used here.
y.push(42);
*y += 100;
// The mutable reference `y` is not used anymore,
// therefore a new reference can be created.
let z = &mut x;
z.push(13);
assert_eq!(x, [42, 13]);
*z += 1000;
assert_eq!(x, 1200);
}
}

View File

@ -1,5 +1,13 @@
#![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.
@ -13,11 +21,3 @@ fn string_uppercase(mut data: String) {
println!("{data}");
}
fn main() {
let data = "Rust is great!".to_string();
get_char(&data);
string_uppercase(data);
}

View File

@ -1,16 +1,14 @@
#[derive(Debug)]
enum Message {
Resize,
Move,
Echo,
ChangeColor,
Quit,
Echo,
Move,
ChangeColor,
}
fn main() {
println!("{:?}", Message::Resize);
println!("{:?}", Message::Move);
println!("{:?}", Message::Echo);
println!("{:?}", Message::ChangeColor);
println!("{:?}", Message::Quit);
println!("{:?}", Message::Echo);
println!("{:?}", Message::Move);
println!("{:?}", Message::ChangeColor);
}

View File

@ -1,13 +1,7 @@
#[derive(Debug)]
struct Point {
x: u64,
y: u64,
}
#[allow(dead_code)]
#[derive(Debug)]
enum Message {
Resize { width: u64, height: u64 },
Move(Point),
Move { x: i64, y: i64 },
Echo(String),
ChangeColor(u8, u8, u8),
Quit,
@ -21,11 +15,7 @@ impl Message {
fn main() {
let messages = [
Message::Resize {
width: 10,
height: 30,
},
Message::Move(Point { x: 10, y: 15 }),
Message::Move { x: 10, y: 30 },
Message::Echo(String::from("hello world")),
Message::ChangeColor(200, 255, 255),
Message::Quit,

View File

@ -1,53 +1,44 @@
struct Point {
x: u64,
y: u64,
}
enum Message {
Resize { width: u64, height: u64 },
Move(Point),
Echo(String),
ChangeColor(u8, u8, u8),
Echo(String),
Move(Point),
Quit,
}
struct Point {
x: u8,
y: u8,
}
struct State {
width: u64,
height: u64,
position: Point,
message: String,
color: (u8, u8, u8),
position: Point,
quit: bool,
message: String,
}
impl State {
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 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 move_position(&mut self, point: Point) {
self.position = point;
}
fn process(&mut self, message: Message) {
match message {
Message::Resize { width, height } => self.resize(width, height),
Message::ChangeColor(r, g, b) => self.change_color((r, g, b)),
Message::Echo(s) => self.echo(s),
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(),
}
}
@ -64,29 +55,21 @@ mod tests {
#[test]
fn test_match_message_call() {
let mut state = State {
width: 0,
height: 0,
position: Point { x: 0, y: 0 },
message: String::from("hello world"),
color: (0, 0, 0),
quit: false,
position: Point { x: 0, y: 0 },
color: (0, 0, 0),
message: 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::Echo(String::from("Hello world!")));
state.process(Message::Move(Point { x: 10, y: 15 }));
state.process(Message::Quit);
assert_eq!(state.width, 10);
assert_eq!(state.height, 30);
assert_eq!(state.color, (255, 0, 255));
assert_eq!(state.position.x, 10);
assert_eq!(state.position.y, 15);
assert_eq!(state.message, "Hello world!");
assert_eq!(state.color, (255, 0, 255));
assert!(state.quit);
assert_eq!(state.message, "Hello world!");
}
}

View File

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

View File

@ -1,7 +1,6 @@
fn string_slice(arg: &str) {
println!("{arg}");
}
fn string(arg: String) {
println!("{arg}");
}
@ -18,11 +17,12 @@ 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 as `&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"));

View File

@ -1,3 +1,4 @@
#[allow(dead_code)]
mod delicious_snacks {
// Added `pub` and used the expected alias after `as`.
pub use self::fruits::PEAR as fruit;

View File

@ -1,7 +1,7 @@
// A basket of fruits in the form of a hash map needs to be defined. The key
// represents the name of the fruit and the value represents how many of that
// particular fruit is in the basket. You have to put at least 3 different
// types of fruits (e.g. apple, banana, mango) in the basket and the total count
// types of fruits (e.g apple, banana, mango) in the basket and the total count
// of all the fruits should be at least 5.
use std::collections::HashMap;

View File

@ -5,8 +5,7 @@
// 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 the fruits that are already in the basket (Apple,
// Mango, and Lychee).
// to insert any more of these fruits!
use std::collections::HashMap;

View File

@ -10,14 +10,14 @@ use std::collections::HashMap;
// A structure to store the goal details of a team.
#[derive(Default)]
struct TeamScores {
struct Team {
goals_scored: u8,
goals_conceded: u8,
}
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
fn build_scores_table(results: &str) -> HashMap<&str, Team> {
// The name of the team is the key and its associated struct is the value.
let mut scores = HashMap::<&str, TeamScores>::new();
let mut scores = HashMap::new();
for line in results.lines() {
let mut split_iterator = line.split(',');
@ -28,13 +28,13 @@ fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
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_default();
let team_1 = scores.entry(team_1_name).or_insert_with(Team::default);
// Update the values.
team_1.goals_scored += team_1_score;
team_1.goals_conceded += team_2_score;
// Similarly for the second team.
let team_2 = scores.entry(team_2_name).or_default();
// Similarely for the second team.
let team_2 = scores.entry(team_2_name).or_insert_with(Team::default);
team_2.goals_scored += team_2_score;
team_2.goals_conceded += team_1_score;
}
@ -60,11 +60,9 @@ England,Spain,1,0";
fn build_scores() {
let scores = build_scores_table(RESULTS);
assert!(
["England", "France", "Germany", "Italy", "Poland", "Spain"]
.into_iter()
.all(|team_name| scores.contains_key(team_name))
);
assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"]
.into_iter()
.all(|team_name| scores.contains_key(team_name)));
}
#[test]

View File

@ -1,8 +1,8 @@
// This function returns how much ice cream there is left in the fridge.
// This function returns how much icecream there is left in the fridge.
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
// someone eats it all, so no ice cream is left (value 0). Return `None` if
// someone eats it all, so no icecream is left (value 0). Return `None` if
// `hour_of_day` is higher than 23.
fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
match hour_of_day {
0..=21 => Some(5),
22..=23 => Some(0),
@ -21,19 +21,19 @@ mod tests {
#[test]
fn raw_value() {
// Using `unwrap` is fine in a test.
let ice_creams = maybe_ice_cream(12).unwrap();
let icecreams = maybe_icecream(12).unwrap();
assert_eq!(ice_creams, 5);
assert_eq!(icecreams, 5);
}
#[test]
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);
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);
}
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
use std::cmp::Ordering;
#![allow(clippy::comparison_chain)]
#[derive(PartialEq, Debug)]
enum CreationError {
@ -11,10 +11,12 @@ struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
match value.cmp(&0) {
Ordering::Less => Err(CreationError::Negative),
Ordering::Equal => Err(CreationError::Zero),
Ordering::Greater => Ok(Self(value as u64)),
if value == 0 {
Err(CreationError::Zero)
} else if value < 0 {
Err(CreationError::Negative)
} else {
Ok(Self(value as u64))
}
}
}

View File

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

View File

@ -24,26 +24,11 @@ impl ParsePosNonzeroError {
Self::Creation(err)
}
fn from_parse_int(err: ParseIntError) -> Self {
fn from_parseint(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);
@ -59,7 +44,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_parse_int)?;
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
}

View File

@ -1,3 +1,5 @@
#![allow(dead_code)]
trait Licensed {
fn licensing_info(&self) -> String {
"Default license".to_string()

View File

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

View File

@ -1,10 +1,14 @@
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() {
let string1 = String::from("long string is long");
// Solution 1: You can move `strings2` out of the inner block so that it is
// Solution1: You can move `strings2` out of the inner block so that it is
// not dropped before the print statement.
let string2 = String::from("xyz");
let result;
@ -21,7 +25,7 @@ fn main() {
{
let string2 = String::from("xyz");
result = longest(&string1, &string2);
// Solution 2: You can move the print statement into the inner block so
// Solution2: You can move the print statement into the inner block so
// that it is executed before `string2` is dropped.
println!("The longest string is '{result}'");
// `string2` dropped here (end of the inner scope).

View File

@ -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 must be positive");
panic!("Rectangle width and height can't be negative");
}
Rectangle { width, height }

View File

@ -1,10 +1,6 @@
#[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,
}
@ -13,10 +9,6 @@ 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);
}
@ -52,8 +44,6 @@ mod tests {
#[test]
fn test_success() {
assert_eq!(divide(81, 9), Ok(9));
assert_eq!(divide(81, -1), Ok(-81));
assert_eq!(divide(i64::MIN, i64::MIN), Ok(1));
}
#[test]
@ -61,11 +51,6 @@ 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));

View File

@ -25,7 +25,6 @@ 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)
}

View File

@ -47,23 +47,6 @@ 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.
}
@ -71,9 +54,10 @@ fn main() {
#[cfg(test)]
mod tests {
use super::*;
use Progress::*;
fn get_map() -> HashMap<String, Progress> {
use Progress::*;
let mut map = HashMap::new();
map.insert(String::from("variables1"), Complete);
map.insert(String::from("functions1"), Complete);
@ -86,6 +70,8 @@ mod tests {
}
fn get_vec_map() -> Vec<HashMap<String, Progress>> {
use Progress::*;
let map = get_map();
let mut other = HashMap::new();
@ -101,25 +87,25 @@ mod tests {
#[test]
fn count_complete() {
let map = get_map();
assert_eq!(count_iterator(&map, Complete), 3);
assert_eq!(count_iterator(&map, Progress::Complete), 3);
}
#[test]
fn count_some() {
let map = get_map();
assert_eq!(count_iterator(&map, Some), 1);
assert_eq!(count_iterator(&map, Progress::Some), 1);
}
#[test]
fn count_none() {
let map = get_map();
assert_eq!(count_iterator(&map, None), 2);
assert_eq!(count_iterator(&map, Progress::None), 2);
}
#[test]
fn count_complete_equals_for() {
let map = get_map();
let progress_states = [Complete, Some, None];
let progress_states = [Progress::Complete, Progress::Some, Progress::None];
for progress_state in progress_states {
assert_eq!(
count_for(&map, progress_state),
@ -131,38 +117,34 @@ mod tests {
#[test]
fn count_collection_complete() {
let collection = get_vec_map();
assert_eq!(count_collection_iterator(&collection, Complete), 6);
assert_eq!(count_collection_iterator_flat(&collection, Complete), 6);
assert_eq!(
count_collection_iterator(&collection, Progress::Complete),
6,
);
}
#[test]
fn count_collection_some() {
let collection = get_vec_map();
assert_eq!(count_collection_iterator(&collection, Some), 1);
assert_eq!(count_collection_iterator_flat(&collection, Some), 1);
assert_eq!(count_collection_iterator(&collection, Progress::Some), 1);
}
#[test]
fn count_collection_none() {
let collection = get_vec_map();
assert_eq!(count_collection_iterator(&collection, None), 4);
assert_eq!(count_collection_iterator_flat(&collection, None), 4);
assert_eq!(count_collection_iterator(&collection, Progress::None), 4);
}
#[test]
fn count_collection_equals_for() {
let collection = get_vec_map();
let progress_states = [Complete, Some, None];
let progress_states = [Progress::Complete, Progress::Some, Progress::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),
);
}
}
}

View File

@ -45,9 +45,8 @@ mod tests {
#[test]
fn owned_no_mutation() {
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
// 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.
// case, no mutation occurs 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);
@ -57,9 +56,9 @@ mod tests {
#[test]
fn owned_mutation() {
// 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.
// 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.
let vec = vec![-1, 0, 1];
let mut input = Cow::from(vec);
abs_all(&mut input);

View File

@ -8,6 +8,7 @@ use std::rc::Rc;
#[derive(Debug)]
struct Sun;
#[allow(dead_code)]
#[derive(Debug)]
enum Planet {
Mercury(Rc<Sun>),
@ -63,10 +64,12 @@ mod tests {
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
saturn.details();
// TODO
let uranus = Planet::Uranus(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
uranus.details();
// TODO
let neptune = Planet::Neptune(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
neptune.details();

View File

@ -1,5 +1,5 @@
// 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
// 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
// wait until all the spawned threads have finished and should collect their
// return values into a vector.

Some files were not shown because too many files have changed in this diff Show More