Compare commits

..

111 Commits

Author SHA1 Message Date
Geert-Johan Riemer
21f3427c92
Update 03_variants_with_data.md (#264) 2025-03-03 16:32:13 +01:00
Karolis Strazdas
b839c770b5
Fix typos in 08_overflow.md. (#261)
* Fix a typo in "Profiles" section - change "form" to "from". 
* Remove "etc.", as it should not be used together with "e.g.".
2025-02-23 09:57:34 +01:00
dawe
fd23b201fe
add missing word in 03_runtime.md (#259) 2025-02-15 10:09:16 +01:00
David Ekete
13850a6b01
Update 04_scoped_threads.md (#258)
Improved phrasing for  the `scope` h2
2025-02-08 10:21:56 +01:00
LukeMathWalker
a7865baf3c Update redirects 2025-01-30 15:14:11 +01:00
LukeMathWalker
0e1c66814d Update shortened links 2025-01-30 15:07:03 +01:00
Luca Palmieri
2f059fc5ba
Link to wr website 2025-01-30 14:29:47 +01:00
James Judd
63402ef5c2
Fix dead link to blog.acolyer.org (#253)
Co-authored-by: James Judd <james@lucidchart.com>
2025-01-30 14:13:05 +01:00
Luca Palmieri
892c37ead9
Typo. 2025-01-30 14:03:45 +01:00
Gabriel Crispino
c745cf8ddd
fix grammar in exercise 06 from section 03 (#248)
Add missing "to" preposition in first TODO comment
2025-01-19 11:42:03 +01:00
Luca Palmieri
3f60de3712
Avoid using a suffix (#239) 2024-12-18 17:38:05 +01:00
Luca Palmieri
36f6375c20
Change if-else exercise to require an if-else (#238) 2024-12-18 17:33:03 +01:00
Luca Palmieri
fb19005d5f
Clarify Send-Sync relationship (#237) 2024-12-18 17:22:16 +01:00
Luca Palmieri
cc0092b2c6
Clarify that both crate and super are keywords (#236) 2024-12-18 17:13:27 +01:00
Luca Palmieri
d5e1c00108
Give a more structure hint for iteration exercises (#235) 2024-12-18 17:03:24 +01:00
Luca Palmieri
60947aaacd
Showcase else-if (#234) 2024-12-18 16:53:14 +01:00
LukeMathWalker
fe40e6e2d0 Don't mark Rust code snippets as runnable 2024-12-18 16:48:57 +01:00
LukeMathWalker
7fb0910b39 Format all manifests 2024-12-18 16:39:16 +01:00
Luca Palmieri
468b74ba4d
Ignore dead code warnings until visibility has been explained (#232) 2024-12-18 16:38:07 +01:00
LukeMathWalker
b0e1e5a1ec Link to the next tutored session in the methodology section 2024-12-18 11:38:21 +01:00
LukeMathWalker
93536fbcfd Formatting 2024-12-18 11:25:30 +01:00
LukeMathWalker
1993507236 Update redirects 2024-12-18 11:24:29 +01:00
LukeMathWalker
a1a2dc1588 Update aliases 2024-12-18 11:23:03 +01:00
Maor Levy
51cad6bdfe
doc: update book 06_01_arrays (#195) 2024-12-18 11:12:50 +01:00
Daniel Wagner-Hall
ab1eb5d80c
Warn about the risks of using catch-all patterns (#200) 2024-12-18 11:05:01 +01:00
Daniel Wagner-Hall
fd6a56fd84
Add install steps to the welcome page (#196) 2024-12-18 11:02:25 +01:00
Will Bush
0419e2e8b2
Fix typo in 03_leak.md (#220)
Corrected a confusing sentence with double negative.
2024-11-19 09:19:50 +01:00
dawe
4b02e92691
fix a small typo in 11_locks.md (#219) 2024-11-18 10:58:59 +01:00
dawe
eb2d858807
fix a superfluous word in a comment (#217) 2024-11-15 17:53:27 +01:00
Marco Otte-Witte
4194ee9998
Merge pull request #208 from mainmatter/gravity
run Gravity on online PDF
2024-11-12 12:50:23 +01:00
Marco Otte-Witte
9f5e4a0d76
debug 2024-11-12 12:43:03 +01:00
Marco Otte-Witte
da610ee79b
run Gravity on online PDF 2024-11-12 12:29:08 +01:00
Marco Otte-Witte
f1a0eab4a6
Merge pull request #213 from mainmatter/fix-verbosity
switch off Pandoc verbosity
2024-11-12 11:58:47 +01:00
Marco Otte-Witte
3addd64dc2
switch off Pandoc verbosity 2024-11-12 11:51:00 +01:00
Marco Otte-Witte
a53e4f86a1
Merge pull request #212 from mainmatter/debug-build
Fix build
2024-11-12 11:46:58 +01:00
Marco Otte-Witte
5edddf102e
fix reference to fallback font 2024-11-12 11:41:00 +01:00
Marco Otte-Witte
dab563a329
debug pandoc build 2024-11-12 11:34:52 +01:00
Marco Otte-Witte
c75ff98c6a
format 2024-11-12 11:17:13 +01:00
Robertino
b60e021ab1
Update 11_clone.md (#205)
typo in code
2024-11-01 08:22:23 +01:00
Henk Oordt
9e63a5cdf6
Merge pull request #190 from Manubi/patch-1 2024-10-29 15:48:35 +01:00
Manuel Bichler
f4a7472f30
Update 08_overflow.md
Added some context.
2024-10-29 15:40:56 +01:00
Henk Oordt
04b89ff26e
Merge pull request #188 from cenviity/push-usrtukunqysw
exercises 02_basic_calculator/04_panics: fix comma splice in panic message
2024-10-29 14:39:03 +01:00
Henk Oordt
26e78072cd
Merge pull request #187 from half2me/patch-1
Fix docstring: "when converted to `u8`"
2024-10-29 14:30:20 +01:00
Henk Oordt
c540242c15
Merge pull request #172 from marcoow/fix-fork-build
Fix builds for PRs originating from forks
2024-10-29 14:16:29 +01:00
Henk Oordt
12fb62d8be
Merge pull request #192 from mainmatter/ci-schedule
chore(ci): re-create book artifacts at the beginning of each month
2024-10-28 13:33:42 +01:00
Bartlomiej Dudzik
0bf398a351
chore(ci): re-create book artifacts at the beginning of each month 2024-10-28 13:19:50 +01:00
Manuel Bichler
f14a047e8b
Update 08_overflow.md
if you read threw the profile docs it provides 4 profiles. https://doc.rust-lang.org/cargo/reference/profiles.html
2024-10-27 19:32:58 +01:00
Vincent Ging Ho Yim
5e23bee61e exercises 02_basic_calculator/04_panics: fix comma splice in panic message 2024-10-24 15:18:53 +11:00
Benjamin Tamasi
8488317564
Change docstring: "when converted to u8"
Fixed docstring
2024-10-22 13:39:17 +02:00
Henk Oordt
bd17a59e8e
Merge pull request #177 from LucaBoschetto/patch-1
Update reference from Rustonomicon to Rust Reference
2024-10-21 13:27:25 +02:00
Henk Oordt
8c3e40033b
Merge pull request #174 from drguildo/channels-typo-fix
Fix typo
2024-10-21 13:24:04 +02:00
iacri
a392371ef0
05_ticket_v2 outro: correct comment on title (#179)
Characters was meant to be bytes, this was missed in a former refactoring
2024-10-19 09:36:26 +02:00
Luca Boschetto
f4cb4b55d0
Update reference from Rustonomicon to Rust Reference
The exercise refers to the "Data layout" section of the Rustonomicon, but now that section claims itself deprecated and redirects to the "Type layout" section of The Rust Reference.
2024-10-16 18:15:59 +02:00
Simon Morgan
45c32d8e41 Fix typo 2024-10-14 22:41:16 +01:00
Marco Otte-Witte
4ca49fa5fd
use OpenSans as fallback font on PRs from forks 2024-10-14 12:56:17 +02:00
Adalfarus
c437f75fcf
Update lib.rs, added info that needed panic messages can be found in the tests. (#157) 2024-10-09 09:44:47 +02:00
Marco Otte-Witte
f839fc00f9
Merge pull request #161 from mainmatter/cover-for-pdf
add cover and back to downloadable pdf
2024-10-08 11:22:45 +02:00
Marco Otte-Witte
954dd0ff14
add real back and front cover 2024-10-08 11:11:20 +02:00
Marco Otte-Witte
33bcaf0a7a
add cover and back to downloadable pdf 2024-10-02 16:00:32 +02:00
Marco Otte-Witte
0aecb5380e
Merge pull request #159 from mainmatter/style-ebook
simplify ebook styling
2024-10-02 15:40:46 +02:00
Marco Otte-Witte
276bdbbcdc
simplify ebook styling config 2024-10-02 15:30:40 +02:00
Remo Senekowitsch
a899ecec96
Fix typo (#160) 2024-10-02 15:20:17 +02:00
Luca Palmieri
a0537f8c51 chore: Shorten links 2024-09-27 14:47:00 +02:00
Luca Palmieri
73649001ba
Add link to paperback. 2024-09-27 10:13:15 +02:00
David Parra
99ee4b27c5
Typo (#154)
Thanks for such a great book, found a small typo.
2024-09-22 08:12:14 +02:00
Marco Otte-Witte
af6c605fe6
Merge pull request #133 from mainmatter/style-book
Style book
2024-09-19 11:47:52 +02:00
Marco Otte-Witte
f613c9a58b
Merge pull request #150 from mainmatter/LukeMathWalker-patch-1
Don't omit macros from PDF and ePUB versions
2024-09-19 11:40:52 +02:00
Luca Palmieri
e9c4f37130
Don't omit macros from PDF and ePUB versions 2024-09-19 10:33:03 +02:00
Abdelrahman Omar
e768d9dbd8
add test coverage for IndexMut<&TicketId> (#142) 2024-09-11 11:37:23 +02:00
Tobias Hermann
ea71ac2c0a
Remove duplicated mention of slices in 00_intro.md (#144)
Slices are already mentioned in bullet point 4, so they can be removed from bullet point 2.
2024-09-11 11:36:58 +02:00
Marco Otte-Witte
0ca42ba058
move comment to meaningful place 2024-08-27 09:14:20 +02:00
Marco Otte-Witte
681903a853
make fontsize 1pt smaller 2024-08-27 09:13:38 +02:00
Marco Otte-Witte
6bdcf19f5f
use less teal for sh theme 2024-08-22 15:43:19 +02:00
Marco Otte-Witte
297d78d750
actually use custom highlighting theme 2024-08-22 15:38:18 +02:00
Marco Otte-Witte
cab4de6433
remove background color on operators 2024-08-22 15:25:58 +02:00
Marco Otte-Witte
52c21a241d
add lost settings for print version back in 2024-08-22 15:22:03 +02:00
Marco Otte-Witte
982a8f0789
copy config to paperback 2024-08-20 18:26:26 +02:00
Marco Otte-Witte
1b050774e7
custom syntax highlighting 2024-08-20 18:26:26 +02:00
Marco Otte-Witte
d269320825
set link and url styles 2024-08-20 18:26:26 +02:00
Marco Otte-Witte
8730a8458a
use Core Sans font 2024-08-20 18:26:26 +02:00
Henk Oordt
97d13e5c85
Merge pull request #131 from ehershey/grammar
Grammar fixups
2024-08-14 09:38:27 +02:00
Ernie Hershey
d5918375f3
Grammar fixups 2024-08-13 11:41:40 -04:00
Luca Palmieri
fb8f83a518
Add ePUB format (#130) 2024-08-13 15:53:43 +02:00
LukeMathWalker
f9a1d427b2 Adjust margins for paperback version. Ensure nothing breaches the right margin. 2024-08-07 15:39:35 +02:00
LukeMathWalker
05e3efd298 redirects -> _redirects 2024-08-06 17:34:08 +02:00
Luca Palmieri
c9ed60daf5
Add print-ready PDF (#127) 2024-08-06 17:09:04 +02:00
LukeMathWalker
0910f3909f Add formats. 2024-08-06 10:31:06 +02:00
mckzm
2765fdaa0e
Fix reference to supertrait syntax in 09_error_trait.md (#124)
The [Error trait](https://rust-exercises.com/100-exercises/05_ticket_v2/09_error_trait.html?highlight=supertrait#the-error-trait) chapter states that the reader may recall the supertrait syntax from the [Sized trait](https://rust-exercises.com/100-exercises/04_traits/08_sized) chapter. Actually the syntax is introduced in the [From and Into](https://rust-exercises.com/100-exercises/04_traits/09_from) chapter instead. This PR amends the text accordingly.
2024-08-05 17:53:07 +02:00
Luca Palmieri
96f06708b0
Render the book in PDF using pandoc and LaTeX. (#126)
* Render the book in PDF using `pandoc` and LaTeX.

* Fix installs.

* Go the apt-get route

* Another attempt

* Avoid installing twice.

* Re-order.

* Add more packages.

* Minimise deps. Fix link checker.

* Missing package.

* Missing package.

* Missing package.

* More packages.

* Missing package.

* Missing package.

* More packages...

* Remove.

* Fix link checker.

* Fix link checker.

* Fix path.

* Add subtitle.

* Avoid running over the right margin.

* Avoid running over the right margin.

* Formatting
2024-08-05 17:52:15 +02:00
mckzm
e732ea82e4
Fix minor typo in 09_bounded.md (#125)
Trim errant `s` from `enqueue`'s end in [Bounded vs unbounded channels](https://rust-exercises.com/100-exercises/07_threads/09_bounded.html):

`if the producers enqueues messages` -> `if the producers enqueue messages`
2024-08-05 15:03:31 +02:00
LukeMathWalker
5ef0a6aa12 Formatting 2024-08-01 15:33:23 +02:00
LukeMathWalker
f882f0416d Change exercise for mutable slices. Closes #26 2024-08-01 15:33:13 +02:00
LukeMathWalker
6029a8fc17 Ensure that overflow checks are active for the copy exercise. Closes #64 2024-08-01 15:14:10 +02:00
LukeMathWalker
056505d89f It's enough for one field to be private. Closes #69 2024-08-01 15:10:50 +02:00
LukeMathWalker
b039a6c5c2 Formatting 2024-08-01 15:09:40 +02:00
LukeMathWalker
3a9c9ea520 Remove reference to cargo new to avoid confusion. Closes #71. 2024-08-01 15:09:24 +02:00
LukeMathWalker
3f4d31148f Add cargo-modules. Closes #101 2024-08-01 14:56:43 +02:00
LukeMathWalker
2f067058ce Fix #104 2024-08-01 14:54:25 +02:00
LukeMathWalker
be5c0e8bae Reword 'static issues. Closes #117 2024-08-01 14:53:53 +02:00
LukeMathWalker
a6056381bd No need to deploy anymore. 2024-07-30 16:13:20 +02:00
code-cp
59833f2a55
Update 06_async_aware_primitives.md (#122)
Fix a typo
2024-07-28 12:46:20 +02:00
Zhang Zihao
9a2086081c
Fix a typo (#116) 2024-07-17 08:08:22 +02:00
Jack Moffitt
f272843c61
Remove pub visibility on server() as the argument has a private type. This gets rid of a warning. (#112) 2024-07-07 21:18:43 +02:00
Evgeniy Filimonov
fccad08921
07_threads: 03_leak: Leak vector with Vec::leak, not Box::leak (#107) 2024-06-30 18:23:20 +02:00
Palash Nigam (He/Him)
de45f8adf2
Ch-08 Futures Exercise 02: Fix typo (#106) 2024-06-30 00:18:46 +02:00
LOGI
5660a2f7a8
fix(typo): a module name in comments (#102)
The output of the compiler does not include the module name of the `Ticket` struct and the root module of this exercise is `visibility` rather than `encapsulation` which is the root module of the next exercise.
2024-06-27 11:35:11 +02:00
Saqib Ahmed
491319a6d5
fix: fix a typo (#103) 2024-06-27 11:34:02 +02:00
Jerry Wu
83cf1cad62
Update 11_locks.md (#94)
Suggest removing an extra semicolon.
2024-06-20 10:21:53 +02:00
Ernie Hershey
d8d7e73f1c
fix syntax with comma (#89)
Example doesn't compile with a comma here
2024-06-20 10:21:33 +02:00
Onè
468de3c0ac
Change test to require impl (#87)
impl std::ops::Add<&SaturatingU16> for SaturatingU16
2024-06-20 10:21:14 +02:00
tomgrbz
c86360f3c4
Remove array/slice syntax from argument &mut str in TODO comment for lowercase func (#99)
Co-authored-by: thomasgrbic <grbic.t@northeastern.edu>
2024-06-20 10:18:55 +02:00
118 changed files with 1960 additions and 581 deletions

View File

@ -7,6 +7,10 @@ on:
pull_request:
branches:
- main
workflow_dispatch:
schedule:
# First day of a month
- cron: '0 0 1 * *'
jobs:
build:
@ -15,16 +19,93 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get Core Sans
uses: actions/checkout@v4
if: "!github.event.pull_request.head.repo.fork"
with:
fetch-depth: 0
repository: mainmatter/core-sans-a-fonts
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
path: core-sans-a-fonts
- name: Install Core Sans Font
if: "!github.event.pull_request.head.repo.fork"
run: |
sudo cp -r core-sans-a-fonts/* /usr/local/share/fonts/
sudo fc-cache -f -v
fc-list | grep "Core Sans"
- name: Use Fallback font for fork PRs
if: "github.event.pull_request.head.repo.fork"
run: |
sed -i 's/"BoldFont=CoreSansA65.ttf",//g' book/book.toml
sed -i 's/"ItalicFont=CoreSansA45It.ttf",//g' book/book.toml
sed -i 's/"BoldItalicFont=CoreSansA65It.ttf",//g' book/book.toml
sed -i 's/CoreSansA45.ttf/Open Sans:style=Regular/g' book/book.toml
- uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install plugin
- name: Install exercise plugin
run: cargo install --path helpers/mdbook-exercise-linker
- name: Install link shortener plugin
run: cargo install --path helpers/mdbook-link-shortener
- name: Install mdbook-pandoc, calibre, pdftk and related dependencies
run: |
cargo install mdbook-pandoc --locked --version 0.7.1
sudo apt-get update
sudo apt-get install -y fonts-noto fonts-open-sans calibre pdftk
sudo fc-cache -f -v
export PANDOC_VERSION=3.3
curl -LsSf https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-linux-amd64.tar.gz | tar zxf -
echo "$PWD/pandoc-${PANDOC_VERSION}/bin" >> $GITHUB_PATH
shell: bash
- name: Setup TeX Live
uses: teatimeguest/setup-texlive-action@v3
with:
packages:
scheme-basic
luatex
lualatex-math
luacolor
luatexbase
luaotfload
framed
unicode-math
xcolor
geometry
longtable
booktabs
array
lua-ul
etoolbox
fancyvrb
footnote
selnolig
natbib
csquotes
bookmark
xurl
amsmath
setspace
iftex
- name: Check `tlmgr` version
run: tlmgr --version
- uses: taiki-e/install-action@v2
with:
tool: mdbook
- name: Build book
env:
LINK_SHORTENER_VERIFY: "true"
run: |
cd book
mdbook build
- name: Add cover and back to downloadable PDF
run: |
pdftk book/assets/cover.pdf book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf book/assets/back.pdf cat output book/book/pandoc/pdf/100-exercises-to-learn-rust-with-cover.pdf
mv book/book/pandoc/pdf/100-exercises-to-learn-rust-with-cover.pdf book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf
- name: Convert HTML to ePUB
run: |
cd book/book/pandoc/html
sed -i 's|<code>\\newpage</code>{=latex}||g' 100-exercises-to-learn-rust.html
ebook-convert 100-exercises-to-learn-rust.html 100-exercises-to-learn-rust.epub \
--embed-all-fonts \
--subset-embedded-fonts
- name: Link Checker
uses: lycheeverse/lychee-action@v1
with:
@ -33,24 +114,54 @@ jobs:
--exclude-loopback
--require-https
--no-progress
book/book
# Upload the book as an artifact
book/book/html/
# Upload the HTML book as an artifact
- uses: actions/upload-artifact@v4
with:
name: book
path: book/book
# Commit and push all changed files.
# Must only affect files that are listed in "paths-ignore".
- name: Git commit build artifacts
# Only run on main branch push (e.g. pull request merge).
if: github.event_name == 'push'
run: |
git checkout -b deploy
git config --global user.name "Deployer"
git config --global user.email "username@users.noreply.github.com"
git add --force book/book
git commit -m "Render book"
git push --set-upstream --force-with-lease origin deploy
# When you support multiple formats, the output directory changes
# to include the format in its path.
path: book/book/html
- uses: actions/upload-artifact@v4
with:
name: online-pdf
path: book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf
- uses: actions/upload-artifact@v4
with:
name: paperback
path: book/book/pandoc/paperback/100-exercises-to-learn-rust.pdf
- uses: actions/upload-artifact@v4
with:
name: ePUB
path: book/book/pandoc/html/100-exercises-to-learn-rust.epub
is_fresh:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: sudo apt-get update && sudo apt-get install -y jq
- run: |
./helpers/json2redirects.sh book/link2alias.json > site/_redirects
# Verify nothing has changed, meaning that the redirect file is up-to-date
- run: |
git diff --exit-code site/_redirects
gravity:
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/download-artifact@v4
with:
path: book
pattern: online-pdf
- uses: pnpm/action-setup@v4
with:
version: 9
- run: ls -las ./book
- name: Run Gravity
run: pnpm dlx @gravityci/cli "./book/**/*"
env:
GRAVITY_TOKEN: ${{ secrets.GRAVITY_TOKEN }}
formatter:
runs-on: ubuntu-latest

463
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,14 @@
[workspace]
members = ["exercises/*/*", "helpers/common", "helpers/mdbook-exercise-linker", "helpers/ticket_fields"]
members = [
"exercises/*/*",
"helpers/common",
"helpers/mdbook-exercise-linker",
"helpers/mdbook-link-shortener",
"helpers/ticket_fields",
]
resolver = "2"
[profile.dev]
overflow-checks = false
# This is needed to guarantee the expected behaviour on that specific exercise,
# regardless of the "global" setting for `overflow-checks` on the `dev` profile.
[profile.dev.package.copy]
overflow-checks = true

BIN
book/assets/back.pdf Normal file

Binary file not shown.

BIN
book/assets/cover.pdf Normal file

Binary file not shown.

View File

@ -1,12 +1,132 @@
[book]
authors = ["Luca Palmieri (Mainmatter)"]
authors = ["Luca Palmieri"]
language = "en"
multilingual = false
src = "src"
title = "100 Exercises To Learn Rust"
[preprocessor.exercise-linker]
exercise_root_url = "https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises"
[preprocessor.link-shortener]
base_url = "https://ruex.io"
renderers = ["pandoc"]
mapping = "link2alias.json"
verify = false
after = ["exercise-linker"]
[output.html]
git-repository-url = "https://github.com/mainmatter/100-exercises-to-learn-rust"
[preprocessor.exercise-linker]
exercise_root_url = "https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises"
[output.html.playground]
runnable = false
[output.pandoc]
optional = true
hosted-html = "https://rust-exercises.com/100-exercises/"
[output.pandoc.code]
show-hidden-lines = true
[output.pandoc.profile.pdf] # options to pass to Pandoc (see https://pandoc.org/MANUAL.html)
output-file = "100-exercises-to-learn-rust.pdf"
to = "latex"
highlight-style = "./custom.theme"
# We use `lualatext` because, right now, it's the only engine
# that supports fallback fonts, which we need for emojis.
pdf-engine = "lualatex"
metadata-file = "metadata.yml"
[output.pandoc.profile.pdf.variables]
mainfont = "CoreSansA45.ttf"
mainfontoptions = [
"BoldFont=CoreSansA65.ttf",
"ItalicFont=CoreSansA45It.ttf",
"BoldItalicFont=CoreSansA65It.ttf",
]
sansfont = "CoreSansA45.ttf"
sansfontoptions = [
"BoldFont=CoreSansA65.ttf",
"ItalicFont=CoreSansA45It.ttf",
"BoldItalicFont=CoreSansA65It.ttf",
]
# You can get these fonts here: https://fonts.google.com/selection?query=noto+color+
monofont = "Noto Sans Mono"
mainfontfallback = ["Open Sans:style=Regular"]
sansfontfallback = ["Open Sans:style=Regular"]
monofontfallback = [
"Noto Color Emoji:mode=harf",
]
linkcolor = "Links"
urlcolor = "Links"
urlstyle = "rm"
documentclass = "book"
fontsize = "10pt"
geometry = "papersize={8in,10in},top=2cm,bottom=2cm,left=2.4cm,right=2.4cm"
header-includes = [
"\\definecolor{Links}{HTML}{6200EE}",
# Reduce font size of code blocks
"\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\},fontsize=\\small}",
]
[output.pandoc.profile.paperback]
output-file = "100-exercises-to-learn-rust.pdf"
to = "latex"
highlight-style = "monochrome"
metadata-file = "metadata.yml"
# We use `lualatext` because, right now, it's the only engine
# that supports fallback fonts, which we need for emojis.
pdf-engine = "lualatex"
[output.pandoc.profile.paperback.variables]
mainfont = "CoreSansA45.ttf"
mainfontoptions = [
"BoldFont=CoreSansA65.ttf",
"ItalicFont=CoreSansA45It.ttf",
"BoldItalicFont=CoreSansA65It.ttf",
]
sansfont = "CoreSansA45.ttf"
sansfontoptions = [
"BoldFont=CoreSansA65.ttf",
"ItalicFont=CoreSansA45It.ttf",
"BoldItalicFont=CoreSansA65It.ttf",
]
# You can get these fonts here: https://fonts.google.com/selection?query=noto+color+
monofont = "Noto Sans Mono"
mainfontfallback = ["Open Sans:style=Regular"]
sansfontfallback = ["Open Sans:style=Regular"]
monofontfallback = [
"Noto Color Emoji:mode=harf",
]
linkcolor = "Links"
urlcolor = "Links"
urlstyle = "rm"
documentclass = "book"
fontsize = "10pt"
geometry = "papersize={8in,10in},top=2cm,bottom=2cm,left=2.8cm,right=2.5cm"
header-includes = [
"\\definecolor{Links}{HTML}{6200EE}",
# Reduce font size of code blocks
"\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\},fontsize=\\small}",
]
links-as-notes = true
# We go through HTML, rather than directly to ePUB, since routing
# Pandoc's HTML through Calibre's ePUB converter gives us better results.
[output.pandoc.profile.html]
output-file = "100-exercises-to-learn-rust.html"
to = "html"
highlight-style = "monochrome"
embed-resources = true
standalone = true
metadata-file = "metadata.yml"
[output.pandoc.profile.html.variables]
# You can get these fonts here: https://fonts.google.com/selection?query=noto+color+
monofont = "Noto Sans Mono"
mainfontfallback = ["Open Sans:style=Regular"]
sansfontfallback = ["Open Sans:style=Regular"]
monofontfallback = [
"Noto Color Emoji:mode=harf",
]
urlstyle = "rm"

211
book/custom.theme Normal file
View File

@ -0,0 +1,211 @@
{
"text-color": "#c9d1d9",
"background-color": "#343942",
"line-number-color": null,
"line-number-background-color": null,
"text-styles": {
"Alert": {
"text-color": "#ff0000",
"background-color": null,
"bold": true,
"italic": false,
"underline": false
},
"Annotation": {
"text-color": "#8b949e",
"background-color": null,
"bold": true,
"italic": true,
"underline": false
},
"Attribute": {
"text-color": "#03DAC5",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"BaseN": {
"text-color": "#a5d6ff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"BuiltIn": {
"text-color": "#03DAC5",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Char": {
"text-color": "#a5d6ff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Comment": {
"text-color": "#8b949e",
"background-color": null,
"bold": false,
"italic": true,
"underline": false
},
"CommentVar": {
"text-color": "#8b949e",
"background-color": null,
"bold": true,
"italic": true,
"underline": false
},
"Constant": {
"text-color": "#79c0ff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"ControlFlow": {
"text-color": "#03DAC5",
"background-color": null,
"bold": true,
"italic": false,
"underline": false
},
"DataType": {
"text-color": "#8b949e",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"DecVal": {
"text-color": "#79c0ff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Documentation": {
"text-color": "#8b949e",
"background-color": null,
"bold": false,
"italic": true,
"underline": false
},
"Error": {
"text-color": "#ff0000",
"background-color": null,
"bold": true,
"italic": false,
"underline": false
},
"Extension": {
"text-color": null,
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Float": {
"text-color": "#79c0ff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Function": {
"text-color": "#d2a8ff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Import": {
"text-color": "#8b949e",
"background-color": null,
"bold": true,
"italic": false,
"underline": false
},
"Information": {
"text-color": "#8b949e",
"background-color": null,
"bold": true,
"italic": true,
"underline": false
},
"Keyword": {
"text-color": "#03DAC5",
"background-color": null,
"bold": true,
"italic": false,
"underline": false
},
"Operator": {
"text-color": "#03DAC5",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Other": {
"text-color": "#007020",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Preprocessor": {
"text-color": "#03DAC5",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"SpecialChar": {
"text-color": "#03DAC5",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"SpecialString": {
"text-color": "#03DAC5",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"String": {
"text-color": "#a5d6ff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Variable": {
"text-color": "#a8daff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"VerbatimString": {
"text-color": "#a5d6ff",
"background-color": null,
"bold": false,
"italic": false,
"underline": false
},
"Warning": {
"text-color": "#60a0b0",
"background-color": null,
"bold": true,
"italic": true,
"underline": false
}
}
}

196
book/link2alias.json Normal file
View File

@ -0,0 +1,196 @@
{
"https://code.visualstudio.com": "f6c",
"https://crates.io": "f4q",
"https://crates.io/crates/cargo-modules": "f2n",
"https://doc.rust-lang.org/book/ch03-02-data-types.html#integer-types": "ffr",
"https://doc.rust-lang.org/book/title-page.html": "f6t",
"https://doc.rust-lang.org/cargo/reference/cargo-targets.html#cargo-targets": "f4m",
"https://doc.rust-lang.org/cargo/reference/profiles.html": "ffc",
"https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html": "f45",
"https://doc.rust-lang.org/nomicon/": "f6u",
"https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast": "f2z",
"https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence": "fzf",
"https://doc.rust-lang.org/reference/lifetime-elision.html": "f4c",
"https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html": "fxy",
"https://doc.rust-lang.org/std/cmp/index.html": "fzm",
"https://doc.rust-lang.org/std/cmp/trait.PartialEq.html": "fzz",
"https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html": "fzb",
"https://doc.rust-lang.org/std/convert/trait.From.html#implementors": "fzp",
"https://doc.rust-lang.org/std/convert/trait.Into.html#implementors": "fzl",
"https://doc.rust-lang.org/std/iter/trait.FusedIterator.html": "f4s",
"https://doc.rust-lang.org/std/iter/trait.Iterator.html": "fxf",
"https://doc.rust-lang.org/std/keyword.for.html": "ffj",
"https://doc.rust-lang.org/std/keyword.while.html": "ffh",
"https://doc.rust-lang.org/std/macro.panic.html": "ffl",
"https://doc.rust-lang.org/std/mem/fn.size_of.html": "f27",
"https://doc.rust-lang.org/std/ops/index.html": "fzn",
"https://doc.rust-lang.org/std/ops/trait.Add.html": "fz4",
"https://doc.rust-lang.org/std/ops/trait.Deref.html#deref-coercion": "fzt",
"https://doc.rust-lang.org/std/ops/trait.Div.html": "fzv",
"https://doc.rust-lang.org/std/ops/trait.Mul.html": "fz6",
"https://doc.rust-lang.org/std/ops/trait.Rem.html": "fz8",
"https://doc.rust-lang.org/std/ops/trait.Sub.html": "fzx",
"https://doc.rust-lang.org/std/prelude/index.html": "f2c",
"https://doc.rust-lang.org/std/primitive.i32.html#associatedconstant.MAX": "ffe",
"https://doc.rust-lang.org/std/primitive.i32.html#associatedconstant.MIN": "ff7",
"https://doc.rust-lang.org/std/primitive.u32.html#associatedconstant.MAX": "ffw",
"https://doc.rust-lang.org/std/slice/struct.Iter.html": "f4d",
"https://doc.rust-lang.org/std/string/struct.String.html": "f26",
"https://doc.rust-lang.org/std/sync/atomic/index.html": "fxh",
"https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter": "f4j",
"https://docs.rs/dhat/latest/dhat/": "f2y",
"https://docs.rs/itertools/": "fx2",
"https://docs.rs/thiserror/latest/thiserror/": "f4n",
"https://docs.rs/tokio-stream/latest/tokio_stream/": "f65",
"https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html#method.merge": "f6m",
"https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html": "f63",
"https://docs.rs/tokio/latest/tokio/task/struct.JoinError.html": "f6z",
"https://docs.rust-embedded.org/book/": "f6k",
"https://en.wikipedia.org/wiki/Dangling_pointer": "f2h",
"https://en.wikipedia.org/wiki/Data_segment": "fx7",
"https://en.wikipedia.org/wiki/Memory_address": "f2r",
"https://en.wikipedia.org/wiki/Stack_overflow": "f2e",
"https://en.wikipedia.org/wiki/Two%27s_complement": "ff9",
"https://en.wikipedia.org/wiki/UTF-8": "f2v",
"https://exercism.io": "f6r",
"https://github.com/LukeMathWalker/cargo-chef": "ffb",
"https://github.com/LukeMathWalker/wiremock-rs": "ffm",
"https://github.com/dtolnay/cargo-expand": "fzq",
"https://github.com/dtolnay/proc-macro-workshop": "fzw",
"https://github.com/mainmatter/100-exercises-to-learn-rust": "ff6",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/01_intro/00_welcome": "ff3",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/01_intro/01_syntax": "ffq",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/00_intro": "ff5",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/01_integers": "fft",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/02_variables": "ffy",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/03_if_else": "ffu",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/04_panics": "ffk",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/05_factorial": "ffs",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/06_while": "ffg",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/07_for": "ffd",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/08_overflow": "f2f",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/09_saturating": "f22",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/02_basic_calculator/10_as_casting": "f24",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/00_intro": "f2x",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/01_struct": "f28",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/02_validation": "f2b",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/03_modules": "f2m",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/04_visibility": "f23",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/05_encapsulation": "f2q",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/06_ownership": "f25",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/07_setters": "f2w",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/08_stack": "f29",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/09_heap": "f2p",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/10_references_in_memory": "f2l",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/11_destructor": "f2g",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/03_ticket_v1/12_outro": "f2j",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/00_intro": "f2d",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/01_trait": "f2a",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/02_orphan_rule": "fz2",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/03_operator_overloading": "fz3",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/04_derive": "fz7",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/05_trait_bounds": "fz9",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/06_str_slice": "fzr",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/07_deref": "fzy",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/08_sized": "fzu",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/09_from": "fzk",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/10_assoc_vs_generic": "fzs",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/11_clone": "fzh",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/12_copy": "fzg",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/13_drop": "fzj",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/04_traits/14_outro": "fzc",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/00_intro": "fza",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/01_enum": "f4f",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/02_match": "f42",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/03_variants_with_data": "f4z",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/04_if_let": "f44",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/05_nullability": "f4x",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/06_fallibility": "f46",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/07_unwrap": "f4v",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/08_error_enums": "f48",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/09_error_trait": "f4b",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/10_packages": "f43",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/11_dependencies": "f4w",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/12_thiserror": "f47",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/13_try_from": "f4e",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/14_source": "f49",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/05_ticket_v2/15_outro": "f4y",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/00_intro": "f4u",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/01_arrays": "f4p",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/02_vec": "f4l",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/03_resizing": "f4k",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/04_iterators": "f4h",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/05_iter": "f4g",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/06_lifetimes": "f4a",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/07_combinators": "fxz",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/08_impl_trait": "fx4",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/09_impl_trait_2": "fxx",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/10_slices": "fx6",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/11_mutable_slices": "fxv",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/12_two_states": "fx8",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/13_index": "fxb",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/14_index_mut": "fxn",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/15_hashmap": "fxm",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/06_ticket_management/16_btreemap": "fx3",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/00_intro": "fxq",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/01_threads": "fxw",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/02_static": "fxe",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/03_leak": "fx9",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/04_scoped_threads": "fxr",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/05_channels": "fxt",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/06_interior_mutability": "fxu",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/07_ack": "fxp",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/08_client": "fxl",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/09_bounded": "fxk",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/10_patch": "fxs",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/11_locks": "fxj",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/12_rw_lock": "fxd",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/13_without_channels": "fxc",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/07_threads/14_sync": "fxa",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/00_intro": "f6f",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/01_async_fn": "f62",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/02_spawn": "f64",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/03_runtime": "f6x",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/04_future": "f66",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/05_blocking": "f68",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/06_async_aware_primitives": "f6b",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/07_cancellation": "f6q",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises/08_futures/08_outro": "f6e",
"https://github.com/mainmatter/100-exercises-to-learn-rust/tree/solutions": "ffz",
"https://github.com/mainmatter/rust-advanced-testing-workshop": "fzd",
"https://github.com/rust-lang/rustlings": "f69",
"https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/": "ffa",
"https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/": "f4r",
"https://mainmatter.com/contact/": "ff2",
"https://mainmatter.com/rust-consulting/": "fff",
"https://mainmatter.github.io/rust-workshop-runner/": "fv2",
"https://marabos.nl/atomics/": "fxg",
"https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer": "f6a",
"https://nostarch.com/rust-rustaceans": "f6p",
"https://owasp.org/www-community/vulnerabilities/Doubly_freeing_memory": "f2k",
"https://owasp.org/www-community/vulnerabilities/Using_freed_memory": "f2s",
"https://pavex.dev": "ffn",
"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=36e5ddbe3b3f741dfa9f74c956622bac": "ffp",
"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=afedf7062298ca8f5a248bc551062eaa": "fx5",
"https://rust-exercises.com/100-exercises-to-learn-rust.pdf": "ffx",
"https://rust-exercises.com/100-exercises/": "ff4",
"https://rust-exercises.com/advanced-testing/": "f6s",
"https://rust-exercises.com/telemetry/": "f6h",
"https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case": "fze",
"https://rust-lang.github.io/wg-async/vision/submitted_stories/status_quo/barbara_battles_buffered_streams.html": "f6w",
"https://ryhl.io/blog/async-what-is-blocking/": "f6v",
"https://ti.to/mainmatter/rust-from-scratch-jan-2025": "fvf",
"https://tokio.rs/tokio/tutorial/select": "f6n",
"https://valgrind.org/docs/manual/dh-manual.html": "f2t",
"https://veykril.github.io/tlborm/": "fz5",
"https://web.archive.org/web/20240517051950/https://blog.acolyer.org/2019/05/28/cheri-abi/": "f2u",
"https://without.boats/blog/the-scoped-task-trilemma/": "f67",
"https://www.amazon.com/dp/B0DJ14KQQG/": "f6g",
"https://www.jetbrains.com/rust/": "f6d",
"https://www.lpalmieri.com/": "ffv",
"https://www.lpalmieri.com/posts/2020-12-11-zero-to-production-6-domain-modelling/": "f4t",
"https://www.oreilly.com/library/view/programming-rust-2nd/9781492052586/": "f6y",
"https://www.rust-lang.org/tools/install": "f6j",
"https://www.youtube.com/playlist?list=PLqbS7AVVErFirH9armw8yXlE6dacF-A6z": "f6l",
"https://zero2prod.com": "ff8"
}

4
book/metadata.yml Normal file
View File

@ -0,0 +1,4 @@
title: "100 Exercises To Learn Rust"
subtitle: "A hands-on course by Mainmatter"
author: "Luca Palmieri"
keywords: ["Rust", "Programming"]

View File

@ -24,14 +24,19 @@ It has been designed to be interactive and hands-on.
to be delivered in a classroom setting, over 4 days: each attendee advances
through the lessons at their own pace, with an experienced instructor providing
guidance, answering questions and diving deeper into the topics as needed.\
If you're interested in attending one of our training sessions, or if you'd like to
bring this course to your company, please [get in touch](https://mainmatter.com/contact/).
You can sign up for the next tutored session on [our website](https://ti.to/mainmatter/rust-from-scratch-jan-2025).
If you'd like to organise a private session for your company, please [get in touch](https://mainmatter.com/contact/).
You can also follow the course on your own, but we recommend you find a friend or
You can also take the courses on your own, but we recommend you find a friend or
a mentor to help you along the way should you get stuck. You can
also find solutions to all exercises in the
find solutions for all exercises in the
[`solutions` branch of the GitHub repository](https://github.com/mainmatter/100-exercises-to-learn-rust/tree/solutions).
## Formats
You can go through the course material [in the browser](https://rust-exercises.com/100-exercises/) or [download it as a PDF file](https://rust-exercises.com/100-exercises-to-learn-rust.pdf), for offline reading.\
If you prefer to have the course material printed out, [buy a paperback copy on Amazon](https://www.amazon.com/dp/B0DJ14KQQG/).
## Structure
On the left side of the screen, you can see that the course is divided into sections.
@ -46,8 +51,7 @@ Before starting the course, make sure to clone the repository to your local mach
# If you have an SSH key set up with GitHub
git clone git@github.com:mainmatter/100-exercises-to-learn-rust.git
# Otherwise, use the HTTPS URL:
#
# git clone https://github.com/mainmatter/100-exercises-to-learn-rust.git
# https://github.com/mainmatter/100-exercises-to-learn-rust.git
```
We also recommend you work on a branch, so you can easily track your progress and pull
@ -63,17 +67,23 @@ Each exercise is structured as a Rust package.
The package contains the exercise itself, instructions on what to do (in `src/lib.rs`), and a test suite to
automatically verify your solution.
### `wr`, the workshop runner
### Tools
To verify your solutions, we've provided a tool that will guide you through the course.
It is the `wr` CLI (short for "workshop runner").
Install it with:
To work through this course, you'll need:
```bash
cargo install --locked workshop-runner
```
- [**Rust**](https://www.rust-lang.org/tools/install).
If `rustup` is already installed on your system, run `rustup update` (or another appropriate command depending on how you installed Rust on your system) to ensure you're running on the latest stable version.
- _(Optional but recommended)_ An IDE with Rust autocompletion support.
We recommend one of the following:
- [RustRover](https://www.jetbrains.com/rust/);
- [Visual Studio Code](https://code.visualstudio.com) with the [`rust-analyzer`](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer) extension.
In a new terminal, navigate back to the top-level folder of the repository.
### Workshop runner, `wr`
To verify your solutions, we've also provided a tool to guide you through the course: the `wr` CLI, short for "workshop runner".
Install `wr` by following the instructions on [its website](https://mainmatter.github.io/rust-workshop-runner/).
Once you have `wr` installed, open a new terminal and navigate to the top-level folder of the repository.
Run the `wr` command to start the course:
```bash

View File

@ -32,7 +32,7 @@ The function's body is enclosed in curly braces `{}`.
In previous exercise, you saw the `greeting` function:
```rust
// `fn` <function_name> ( <input parameters> ) -> <return_type> { <body> }
// `fn` <function_name> ( <input params> ) -> <return_type> { <body> }
fn greeting() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"

View File

@ -36,6 +36,38 @@ if number < 5 {
}
```
### `else if` clauses
Your code drifts more and more to the right when you have multiple `if` expressions, one nested inside the other.
```rust
let number = 3;
if number < 5 {
println!("`number` is smaller than 5");
} else {
if number >= 3 {
println!("`number` is greater than or equal to 3, but smaller than 5");
} else {
println!("`number` is smaller than 3");
}
}
```
You can use the `else if` keyword to combine multiple `if` expressions into a single one:
```rust
let number = 3;
if number < 5 {
println!("`number` is smaller than 5");
} else if number >= 3 {
println!("`number` is greater than or equal to 3, but smaller than 5");
} else {
println!("`number` is smaller than 3");
}
```
## Booleans
The condition in an `if` expression must be of type `bool`, a **boolean**.\

View File

@ -69,16 +69,20 @@ You may be wondering—what is a profile setting? Let's get into that!
A [**profile**](https://doc.rust-lang.org/cargo/reference/profiles.html) is a set of configuration options that can be
used to customize the way Rust code is compiled.
Cargo provides two built-in profiles: `dev` and `release`.\
Cargo provides 4 built-in profiles: `dev`, `release`, `test`, and `bench`.\
The `dev` profile is used every time you run `cargo build`, `cargo run` or `cargo test`. It's aimed at local
development,
therefore it sacrifices runtime performance in favor of faster compilation times and a better debugging experience.\
The `release` profile, instead, is optimized for runtime performance but incurs longer compilation times. You need
to explicitly request via the `--release` flag—e.g. `cargo build --release` or `cargo run --release`.
The `test` profile is the default profile used by `cargo test`. The `test` profile inherits the settings from the `dev` profile.
The `bench` profile is the default profile used by `cargo bench`. The `bench` profile inherits from the `release` profile.
Use `dev` for iterative development and debugging, `release` for optimized production builds,\
`test` for correctness testing, and `bench` for performance benchmarking.
> "Have you built your project in release mode?" is almost a meme in the Rust community.\
> It refers to developers who are not familiar with Rust and complain about its performance on
> social media (e.g. Reddit, Twitter, etc.) before realizing they haven't built their project in
> social media (e.g. Reddit, Twitter) before realizing they haven't built their project in
> release mode.
You can also define custom profiles or customize the built-in ones.

View File

@ -72,7 +72,8 @@ error: literal out of range for `i8`
4 | let a = 255 as i8;
| ^^^
|
= note: the literal `255` does not fit into the type `i8` whose range is `-128..=127`
= note: the literal `255` does not fit into the type `i8`
whose range is `-128..=127`
= help: consider using the type `u8` instead
= note: `#[deny(overflowing_literals)]` on by default
```

View File

@ -48,7 +48,7 @@ You can create an instance of a struct by specifying the values for each field:
// Syntax: <StructName> { <field_name>: <value>, ... }
let ticket = Ticket {
title: "Build a ticket system".into(),
description: "Create a system that can manage tickets across a Kanban board".into(),
description: "A Kanban board".into(),
status: "Open".into()
};
```
@ -130,7 +130,8 @@ let default_config = Configuration::default();
You can use the function call syntax even for methods that take `self` as their first parameter:
```rust
// Function call syntax: <StructName>::<method_name>(<instance>, <parameters>)
// Function call syntax:
// <StructName>::<method_name>(<instance>, <parameters>)
let is_open = Ticket::is_open(ticket);
```

View File

@ -81,10 +81,13 @@ You have to use a **path** pointing to the entity you want to access.
You can compose the path in various ways:
- starting from the root of the current crate, e.g. `crate::module_1::module_2::MyStruct`
- starting from the root of the current crate, e.g. `crate::module_1::MyStruct`
- starting from the parent module, e.g. `super::my_function`
- starting from the current module, e.g. `sub_module_1::MyStruct`
Both `crate` and `super` are **keywords**.\
`crate` refers to the root of the current crate, while `super` refers to the parent of the current module.
Having to write the full path every time you want to refer to a type can be cumbersome.
To make your life easier, you can introduce a `use` statement to bring the entity into scope.
@ -112,3 +115,10 @@ where each name comes from and potentially introducing name conflicts.\
Nonetheless, it can be useful in some cases, like when writing unit tests. You might have noticed
that most of our test modules start with a `use super::*;` statement to bring all the items from the parent module
(the one being tested) into scope.
## Visualizing the module tree
If you're struggling to picture the module tree of your project, you can try using
[`cargo-modules`](https://crates.io/crates/cargo-modules) to visualize it!
Refer to their documentation for installation instructions and usage examples.

View File

@ -23,14 +23,14 @@ To enforce stricter rules, we must keep the fields private[^newtype].
We can then provide public methods to interact with a `Ticket` instance.
Those public methods will have the responsibility of upholding our invariants (e.g. a title must not be empty).
If all fields are private, it is no longer possible to create a `Ticket` instance directly using the struct
If at least one field is private it is no longer possible to create a `Ticket` instance directly using the struct
instantiation syntax:
```rust
// This won't work!
let ticket = Ticket {
title: "Build a ticket system".into(),
description: "Create a system that can manage tickets across a Kanban board".into(),
description: "A Kanban board".into(),
status: "Open".into()
};
```

View File

@ -44,9 +44,11 @@ error[E0382]: use of moved value: `ticket`
| -------- `ticket` moved due to this method call
...
30 | println!("Your next task is: {}", ticket.title());
| ^^^^^^ value used here after move
| ^^^^^^
| value used here after move
|
note: `Ticket::status` takes ownership of the receiver `self`, which moves `ticket`
note: `Ticket::status` takes ownership of the receiver `self`,
which moves `ticket`
--> src/main.rs:12:23
|
12 | pub fn status(self) -> String {
@ -95,8 +97,8 @@ Ownership can be transferred.
If you own a value, for example, you can transfer ownership to another variable:
```rust
let a = "hello, world".to_string(); // <--- `a` is the owner of the String
let b = a; // <--- `b` is now the owner of the String
let a = "hello, world".to_string(); // <- `a` is the owner of the String
let b = a; // <- `b` is now the owner of the String
```
Rust's ownership system is baked into the type system: each function has to declare in its signature
@ -130,9 +132,11 @@ error[E0382]: use of moved value: `ticket`
| -------- `ticket` moved due to this method call
...
30 | println!("Your next task is: {}", ticket.title());
| ^^^^^^ value used here after move
| ^^^^^^
| value used here after move
|
note: `Ticket::status` takes ownership of the receiver `self`, which moves `ticket`
note: `Ticket::status` takes ownership of the receiver `self`,
which moves `ticket`
--> src/main.rs:12:23
|
12 | pub fn status(self) -> String {
@ -199,8 +203,10 @@ fn main() {
active: true,
};
// `b` is a reference to the `version` field of `config`.
// The type of `b` is `&u32`, since it contains a reference to a `u32` value.
// We create a reference by borrowing `config.version`, using the `&` operator.
// The type of `b` is `&u32`, since it contains a reference to
// a `u32` value.
// We create a reference by borrowing `config.version`, using
// the `&` operator.
// Same symbol (`&`), different meaning depending on the context!
let b: &u32 = &config.version;
// ^ The type annotation is not necessary,

View File

@ -50,7 +50,11 @@ It takes ownership of `self`, changes the title, and returns the modified `Ticke
This is how you'd use it:
```rust
let ticket = Ticket::new("Title".into(), "Description".into(), "To-Do".into());
let ticket = Ticket::new(
"Title".into(),
"Description".into(),
"To-Do".into()
);
let ticket = ticket.set_title("New title".into());
```
@ -88,7 +92,11 @@ Nothing is returned.
You'd use it like this:
```rust
let mut ticket = Ticket::new("Title".into(), "Description".into(), "To-Do".into());
let mut ticket = Ticket::new(
"Title".into(),
"Description".into(),
"To-Do".into()
);
ticket.set_title("New title".into());
// Use the modified ticket

View File

@ -18,11 +18,25 @@ the function's arguments, local variables and a few "bookkeeping" values.\
When the function returns, the stack frame is popped off the stack[^stack-overflow].
```text
+-----------------+
func2 | frame for func2 | func2
+-----------------+ is called +-----------------+ returns +-----------------+
| frame for func1 | -----------> | frame for func1 | ---------> | frame for func1 |
+-----------------+ +-----------------+ +-----------------+
+-----------------+
| frame for func1 |
+-----------------+
|
| func2 is
| called
v
+-----------------+
| frame for func2 |
+-----------------+
| frame for func1 |
+-----------------+
|
| func2
| returns
v
+-----------------+
| frame for func1 |
+-----------------+
```
From an operational point of view, stack allocation/de-allocation is **very fast**.\

View File

@ -137,6 +137,6 @@ or [a custom allocator](https://docs.rs/dhat/latest/dhat/)) to inspect the heap
Heap memory will be reserved when you push data into it for the first time.
[^equivalence]: The size of a pointer depends on the operating system too.
In certain environments, a pointer is **larger** than a memory address (e.g. [CHERI](https://blog.acolyer.org/2019/05/28/cheri-abi/)).
In certain environments, a pointer is **larger** than a memory address (e.g. [CHERI](https://web.archive.org/web/20240517051950/https://blog.acolyer.org/2019/05/28/cheri-abi/)).
Rust makes the simplifying assumption that pointers are the same size as memory addresses,
which is true for most modern systems you're likely to encounter.

View File

@ -89,7 +89,8 @@ although a bit more cumbersome to read:
impl ::core::cmp::PartialEq for Ticket {
#[inline]
fn eq(&self, other: &Ticket) -> bool {
self.title == other.title && self.description == other.description
self.title == other.title
&& self.description == other.description
&& self.status == other.status
}
}

View File

@ -94,11 +94,13 @@ fn print_if_even<T>(n: T) {
This code won't compile:
```text
error[E0599]: no method named `is_even` found for type parameter `T` in the current scope
error[E0599]: no method named `is_even` found for type parameter `T`
in the current scope
--> src/lib.rs:2:10
|
1 | fn print_if_even<T>(n: T) {
| - method `is_even` not found for this type parameter
| - method `is_even` not found
| for this type parameter
2 | if n.is_even() {
| ^^^^^^^ method not found in `T`
@ -106,7 +108,9 @@ error[E0277]: `T` doesn't implement `Debug`
--> src/lib.rs:3:19
|
3 | println!("{n:?} is even");
| ^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug`
| ^^^^^
| `T` cannot be formatted using `{:?}` because
| it doesn't implement `Debug`
|
help: consider restricting type parameter `T`
|

View File

@ -72,7 +72,8 @@ You can, for example, create a `&str` from a `String` like this:
```rust
let mut s = String::with_capacity(5);
s.push_str("Hello");
// Create a string slice reference from the `String`, skipping the first byte.
// Create a string slice reference from the `String`,
// skipping the first byte.
let slice: &str = &s[1..];
```

View File

@ -11,7 +11,8 @@ to the pointer: the length of the slice it points to. Going back to the example
```rust
let mut s = String::with_capacity(5);
s.push_str("Hello");
// Create a string slice reference from the `String`, skipping the first byte.
// Create a string slice reference from the `String`,
// skipping the first byte.
let slice: &str = &s[1..];
```

View File

@ -3,7 +3,11 @@
Let's go back to where our string journey started:
```rust
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
let ticket = Ticket::new(
"A title".into(),
"A description".into(),
"To-Do".into()
);
```
We now know enough to start unpacking what `.into()` is doing here.
@ -14,7 +18,11 @@ This is the signature of the `new` method:
```rust
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Self {
pub fn new(
title: String,
description: String,
status: String
) -> Self {
// [...]
}
}
@ -108,7 +116,7 @@ let title = String::from("A title");
We've been primarily using `.into()`, though.\
If you check out the [implementors of `Into`](https://doc.rust-lang.org/std/convert/trait.Into.html#implementors)
you won't find `Into<&str> for String`. What's going on?
you won't find `Into<String> for &str`. What's going on?
`From` and `Into` are **dual traits**.\
In particular, `Into` is implemented for any type that implements `From` using a **blanket implementation**:

View File

@ -56,7 +56,7 @@ that to `consumer` instead.\
## In memory
Let's look at what happened in memory in the example above.
When `let mut s: String::from("hello");` is executed, the memory looks like this:
When `let mut s = String::from("hello");` is executed, the memory looks like this:
```text
s

View File

@ -44,7 +44,8 @@ impl Drop for MyType {
The compiler will complain with this error message:
```text
error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor
error[E0184]: the trait `Copy` cannot be implemented for this type;
the type has a destructor
--> src/lib.rs:2:17
|
2 | #[derive(Clone, Copy)]

View File

@ -14,7 +14,11 @@ pub struct Ticket {
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Self {
pub fn new(
title: String,
description: String,
status: String
) -> Self {
// [...]
}
}

View File

@ -68,3 +68,11 @@ match status {
```
The `_` pattern matches anything that wasn't matched by the previous patterns.
<div class="warning">
By using this catch-all pattern, you _won't_ get the benefits of compiler-driven refactoring.\
If you add a new enum variant, the compiler _won't_ tell you that you're not handling it.
If you're keen on correctness, avoid using catch-alls. Leverage the compiler to re-examine all matching sites and determine how new enum variants should be handled.
</div>

View File

@ -64,7 +64,7 @@ match status {
println!("Assigned to: {}", assigned_to);
},
Status::ToDo | Status::Done => {
println!("Done");
println!("ToDo or Done");
}
}
```
@ -82,7 +82,7 @@ match status {
println!("Assigned to: {}", person);
},
Status::ToDo | Status::Done => {
println!("Done");
println!("ToDo or Done");
}
}
```

View File

@ -8,7 +8,10 @@ impl Ticket {
match &self.status {
Status::InProgress { assigned_to } => assigned_to,
Status::Done | Status::ToDo => {
panic!("Only `In-Progress` tickets can be assigned to someone"),
panic!(
"Only `In-Progress` tickets can be \
assigned to someone"
)
}
}
}
@ -33,7 +36,9 @@ impl Ticket {
if let Status::InProgress { assigned_to } = &self.status {
assigned_to
} else {
panic!("Only `In-Progress` tickets can be assigned to someone");
panic!(
"Only `In-Progress` tickets can be assigned to someone"
);
}
}
}
@ -48,7 +53,9 @@ you can use the `let/else` construct:
impl Ticket {
pub fn assigned_to(&self) -> &str {
let Status::InProgress { assigned_to } = &self.status else {
panic!("Only `In-Progress` tickets can be assigned to someone");
panic!(
"Only `In-Progress` tickets can be assigned to someone"
);
};
assigned_to
}

View File

@ -4,7 +4,11 @@ Let's revisit the `Ticket::new` function from the previous exercise:
```rust
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> Ticket {
pub fn new(
title: String,
description: String,
status: Status
) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
@ -70,8 +74,9 @@ Rust, with `Result`, forces you to **encode fallibility in the function's signat
If a function can fail (and you want the caller to have a shot at handling the error), it must return a `Result`.
```rust
// Just by looking at the signature, you know that this function can fail.
// You can also inspect `ParseIntError` to see what kind of failures to expect.
// Just by looking at the signature, you know that this function
// can fail. You can also inspect `ParseIntError` to see what
// kind of failures to expect.
fn parse_int(s: &str) -> Result<i32, ParseIntError> {
// ...
}

View File

@ -14,8 +14,8 @@ fn parse_int(s: &str) -> Result<i32, ParseIntError> {
}
// This won't compile: we're not handling the error case.
// We must either use `match` or one of the combinators provided by `Result`
// to "unwrap" the success value or handle the error.
// We must either use `match` or one of the combinators provided by
// `Result` to "unwrap" the success value or handle the error.
let number = parse_int("42") + 2;
```

View File

@ -22,7 +22,7 @@ that implements the `Error` trait.
pub trait Error: Debug + Display {}
```
You might recall the `:` syntax from [the `Sized` trait](../04_traits/08_sized.md)—it's used to specify **supertraits**.
You might recall the `:` syntax from [the `From` trait](../04_traits/09_from.md#supertrait--subtrait)—it's used to specify **supertraits**.
For `Error`, there are two supertraits: `Debug` and `Display`. If a type wants to implement `Error`, it must also
implement `Debug` and `Display`.

View File

@ -46,18 +46,3 @@ You can override these defaults by explicitly declaring your targets in the `Car
[`cargo`'s documentation](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#cargo-targets) for more details.
Keep in mind that while a package can contain multiple crates, it can only contain one library crate.
## Scaffolding a new package
You can use `cargo` to scaffold a new package:
```bash
cargo new my-binary
```
This will create a new folder, `my-binary`, containing a new Rust package with the same name and a single
binary crate inside. If you want to create a library crate instead, you can use the `--lib` flag:
```bash
cargo new my-library --lib
```

View File

@ -8,7 +8,7 @@ store and retrieve tickets.
The task will give us an opportunity to explore new Rust concepts, such as:
- Stack-allocated arrays
- `Vec`, a growable array type, and slices
- `Vec`, a growable array type
- `Iterator` and `IntoIterator`, for iterating over collections
- Slices (`&[T]`), to work with parts of a collection
- Lifetimes, to describe how long references are valid

View File

@ -21,6 +21,15 @@ let numbers: [u32; 3] = [1, 2, 3];
This creates an array of 3 integers, initialized with the values `1`, `2`, and `3`.\
The type of the array is `[u32; 3]`, which reads as "an array of `u32`s with a length of 3".
If all array elements are the same, you can use a shorter syntax to initialize it:
```rust
// [ <value> ; <number of elements> ]
let numbers: [u32; 3] = [1; 3];
```
`[1; 3]` creates an array of three elements, all equal to `1`.
### Accessing elements
You can access elements of an array using square brackets:

View File

@ -1,7 +1,7 @@
# Combinators
Iterators can do so much more than `for` loops!\
If you look at the documentation for the `Iterator` trait, you'll find a **vast** collections of
If you look at the documentation for the `Iterator` trait, you'll find a **vast** collection of
methods that you can leverage to transform, filter, and combine iterators in various ways.
Let's mention the most common ones:

View File

@ -6,7 +6,7 @@ and why we might want to use them.
## What is a thread?
A **thread** is an execution context managed by the underlying operating system.\
Each thread has its own stack, instruction pointer, and program counter.
Each thread has its own stack and instruction pointer.
A single **process** can manage multiple threads.
These threads share the same memory space, which means they can access the same data.

View File

@ -11,9 +11,9 @@ error[E0597]: `v` does not live long enough
...
15 | let right = &v[split_point..];
| ^ borrowed value does not live long enough
16 | let left_handle = thread::spawn(move || left.iter().sum::<i32>());
| ------------------------------------------------
argument requires that `v` is borrowed for `'static`
16 | let left_handle = spawn(move || left.iter().sum::<i32>());
| --------------------------------
argument requires that `v` is borrowed for `'static`
19 | }
| - `v` dropped here while still borrowed
```

View File

@ -27,18 +27,18 @@ run out and crash with an out-of-memory error.
fn oom_trigger() {
loop {
let v: Vec<usize> = Vec::with_capacity(1024);
Box::leak(v);
v.leak();
}
}
```
At the same time, memory leaked via `Box::leak` is not truly forgotten.\
At the same time, memory leaked via `leak` method is not truly forgotten.\
The operating system can map each memory region to the process responsible for it.
When the process exits, the operating system will reclaim that memory.
Keeping this in mind, it can be OK to leak memory when:
- The amount of memory you need to leak is not unbounded/known upfront, or
- The amount of memory you need to leak is bounded/known upfront, or
- Your process is short-lived and you're confident you won't exhaust
all the available memory before it exits

View File

@ -27,7 +27,7 @@ Let's unpack what's happening.
## `scope`
The `std::thread::scope` function creates a new **scope**.\
`std::thread::scope` takes as input a closure, with a single argument: a `Scope` instance.
`std::thread::scope` takes a closure as input, with a single argument: a `Scope` instance.
## Scoped spawns

View File

@ -3,7 +3,7 @@
So far we've been using unbounded channels.\
You can send as many messages as you want, and the channel will grow to accommodate them.\
In a multi-producer single-consumer scenario, this can be problematic: if the producers
enqueues messages at a faster rate than the consumer can process them, the channel will
enqueue messages at a faster rate than the consumer can process them, the channel will
keep growing, potentially consuming all available memory.
Our recommendation is to **never** use an unbounded channel in a production system.\

View File

@ -10,9 +10,9 @@ In the non-threaded version of the system, updates were fairly straightforward:
## Multithreaded updates
The same strategy won't work in the current multi-threaded version,
because the mutable reference would have to be sent over a channel. The borrow checker would
stop us, because `&mut Ticket` doesn't satisfy the `'static` lifetime requirement of `SyncSender::send`.
The same strategy won't work in the current multithreaded version. The borrow checker would
stop us: `SyncSender<&mut Ticket>` isn't `'static` because `&mut Ticket` doesn't satisfy the `'static` lifetime, therefore
they can't be captured by the closure that gets passed to `std::thread::spawn`.
There are a few ways to work around this limitation. We'll explore a few of them in the following exercises.

View File

@ -58,7 +58,7 @@ drop(guard)
## Locking granularity
What should our `Mutex` wrap?\
The simplest option would be the wrap the entire `TicketStore` in a single `Mutex`.\
The simplest option would be to wrap the entire `TicketStore` in a single `Mutex`.\
This would work, but it would severely limit the system's performance: you wouldn't be able to read tickets in parallel,
because every read would have to wait for the lock to be released.\
This is known as **coarse-grained locking**.
@ -99,7 +99,7 @@ fn main() {
let guard = lock.lock().unwrap();
spawn(move || {
receiver.recv().unwrap();;
receiver.recv().unwrap();
});
// Try to send the guard over the channel
@ -111,19 +111,23 @@ fn main() {
The compiler is not happy with this code:
```text
error[E0277]: `MutexGuard<'_, i32>` cannot be sent between threads safely
error[E0277]: `MutexGuard<'_, i32>` cannot be sent between
threads safely
--> src/main.rs:10:7
|
10 | spawn(move || {
| _-----_^
| | |
| | required by a bound introduced by this call
11 | | receiver.recv().unwrap();;
11 | | receiver.recv().unwrap();
12 | | });
| |_^ `MutexGuard<'_, i32>` cannot be sent between threads safely
|
= help: the trait `Send` is not implemented for `MutexGuard<'_, i32>`, which is required by `{closure@src/main.rs:10:7: 10:14}: Send`
= note: required for `std::sync::mpsc::Receiver<MutexGuard<'_, i32>>` to implement `Send`
= help: the trait `Send` is not implemented for
`MutexGuard<'_, i32>`, which is required by
`{closure@src/main.rs:10:7: 10:14}: Send`
= note: required for `Receiver<MutexGuard<'_, i32>>`
to implement `Send`
note: required because it's used within this closure
```

View File

@ -5,20 +5,20 @@ Before we wrap up this chapter, let's talk about another key trait in Rust's sta
`Sync` is an auto trait, just like `Send`.\
It is automatically implemented by all types that can be safely **shared** between threads.
In order words: `T: Sync` means that `&T` is `Send`.
In order words: `T` is Sync if `&T` is `Send`.
## `Sync` doesn't imply `Send`
## `T: Sync` doesn't imply `T: Send`
It's important to note that `Sync` doesn't imply `Send`.\
It's important to note that `T` can be `Sync` without being `Send`.\
For example: `MutexGuard` is not `Send`, but it is `Sync`.
It isn't `Send` because the lock must be released on the same thread that acquired it, therefore we don't
want `MutexGuard` to be dropped on a different thread.\
But it is `Sync`, because giving a `&MutexGuard` to another thread has no impact on where the lock is released.
## `Send` doesn't imply `Sync`
## `T: Send` doesn't imply `T: Sync`
The opposite is also true: `Send` doesn't imply `Sync`.\
The opposite is also true: `T` can be `Send` without being `Sync`.\
For example: `RefCell<T>` is `Send` (if `T` is `Send`), but it is not `Sync`.
`RefCell<T>` performs runtime borrow checking, but the counters it uses to track borrows are not thread-safe.

View File

@ -107,7 +107,7 @@ asynchronous programming in Rust.
The entrypoint of your executable, the `main` function, must be a synchronous function.
That's where you're supposed to set up and launch your chosen async runtime.
Most runtimes provides a macro to make this easier. For `tokio`, it's `tokio::main`:
Most runtimes provide a macro to make this easier. For `tokio`, it's `tokio::main`:
```rust
#[tokio::main]

View File

@ -112,7 +112,7 @@ pub async fn work() {
### `std::thread::spawn` vs `tokio::spawn`
You can think of `tokio::spawn` as the asynchronous sibling of `std::spawn::thread`.
You can think of `tokio::spawn` as the asynchronous sibling of `std::thread::spawn`.
Notice a key difference: with `std::thread::spawn`, you're delegating control to the OS scheduler.
You're not in control of how threads are scheduled.

View File

@ -26,7 +26,7 @@ at any given time.
### Multithreaded runtime
When using the multithreaded runtime, instead, there can up to `N` tasks running
When using the multithreaded runtime, instead, there can be up to `N` tasks running
_in parallel_ at any given time, where `N` is the number of threads used by the
runtime. By default, `N` matches the number of available CPU cores.
@ -41,7 +41,7 @@ across threads.
## Implications
`tokio::spawn` is flavor-agnostic: it'll work no matter if you're running on the multithreaded
or current-thread runtime. The downside is that the signature assume the worst case
or current-thread runtime. The downside is that the signature assumes the worst case
(i.e. multithreaded) and is constrained accordingly:
```rust

View File

@ -47,7 +47,8 @@ The compiler will reject this code:
error: future cannot be sent between threads safely
|
5 | tokio::spawn(example());
| ^^^^^^^^^ future returned by `example` is not `Send`
| ^^^^^^^^^
| future returned by `example` is not `Send`
|
note: future is not `Send` as this value is used across an await
|
@ -55,7 +56,8 @@ note: future is not `Send` as this value is used across an await
| -------- has type `Rc<i32>` which is not `Send`
12 | // A `.await` point
13 | yield_now().await;
| ^^^^^ await occurs here, with `non_send` maybe used later
| ^^^^^
| await occurs here, with `non_send` maybe used later
note: required by a bound in `tokio::spawn`
|
164 | pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
@ -84,7 +86,10 @@ trait Future {
type Output;
// Ignore `Pin` and `Context` for now
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
fn poll(
self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<Self::Output>;
}
```

View File

@ -52,7 +52,7 @@ Yields to runtime
Tries to acquire lock
```
We have a deadlock. Task B we'll never manage to acquire the lock, because the lock
We have a deadlock. Task B will never manage to acquire the lock, because the lock
is currently held by task A, which has yielded to the runtime before releasing the
lock and won't be scheduled again because the runtime cannot preempt task B.

View File

@ -105,5 +105,5 @@ async fn run() {
Check out [`select!`'s documentation](https://tokio.rs/tokio/tutorial/select) for more details.\
If you need to interleave two asynchronous streams of data (e.g. a socket and a channel), prefer using
[`StreamExt::merge`](https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html#method.merge) instead.
- Rather than "abrupt" cancellation, it can be preferable to rely
on [`CancellationToken`](https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html).
- A [`CancellationToken`](https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html) may be
preferable to `JoinHandle::abort` in some cases.

View File

@ -48,5 +48,5 @@ check out the [Embedded Rust book](https://docs.rust-embedded.org/book/).
You can then find resources on key topics that cut across domains.\
For testing, check out
["Advanced testing, going beyond the basics"](https://github.com/mainmatter/rust-advanced-testing-workshop).\
For telemetry, check out ["You can't fix what you can't see"](https://github.com/mainmatter/rust-telemetry-workshop).
["Advanced testing, going beyond the basics"](https://rust-exercises.com/advanced-testing/).\
For telemetry, check out ["You can't fix what you can't see"](https://rust-exercises.com/telemetry/).

View File

@ -2,3 +2,9 @@
name = "welcome_00"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -17,7 +17,7 @@
// You can also find solutions to all exercises in the `solutions` git branch.
fn greeting() -> &'static str {
// TODO: fix me 👇
"I'm ready to learn Rust!"
"I'm ready to __!"
}
// Your solutions will be automatically verified by a set of tests.

View File

@ -2,3 +2,9 @@
name = "syntax"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -3,7 +3,7 @@
// partner in this course and it'll often guide you in the right direction!
//
// The input parameters should have the same type of the return type.
fn compute(a: u32, b: u32) -> u32 {
fn compute(a, b) -> u32 {
// Don't touch the function body.
a + b * 2
}
@ -16,4 +16,4 @@ mod tests {
fn case() {
assert_eq!(compute(1, 2), 5);
}
}
}

View File

@ -2,3 +2,9 @@
name = "intro_01"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -1,6 +1,6 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to build a calculator in Rust!"
"I'm ready to __!"
}
#[cfg(test)]

View File

@ -2,3 +2,9 @@
name = "integers"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -1,6 +1,7 @@
fn compute(a: u32, b: u32) -> u32 {
// TODO: change the line below to fix the compiler error and make the tests pass.
a + b * 4u32
let multiplier: u8 = 4;
a + b * multiplier
}
#[cfg(test)]

View File

@ -2,3 +2,9 @@
name = "variables"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -8,7 +8,7 @@
pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: define a variable named `distance` with the right value to get tests to pass
// Do you need to annotate the type of `distance`? Why or why not?
let distance: u32 = end - start;
// Don't change the line below
distance / time_elapsed
}

View File

@ -2,3 +2,9 @@
name = "if_else"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -1,24 +1,36 @@
/// Return `true` if `n` is even, `false` otherwise.
fn is_even(n: u32) -> bool {
n % 2 == 0
/// Return `12` if `n` is even,
/// `13` if `n` is divisible by `3`,
/// `17` otherwise.
fn magic_number(n: u32) -> u32 {
todo!()
}
#[cfg(test)]
mod tests {
use crate::is_even;
use crate::magic_number;
#[test]
fn one() {
assert!(!is_even(1));
assert_eq!(magic_number(1), 17);
}
#[test]
fn two() {
assert!(is_even(2));
assert_eq!(magic_number(2), 12);
}
#[test]
fn six() {
assert_eq!(magic_number(6), 12);
}
#[test]
fn nine() {
assert_eq!(magic_number(9), 13);
}
#[test]
fn high() {
assert!(!is_even(231));
assert_eq!(magic_number(233), 17);
}
}

View File

@ -2,3 +2,9 @@
name = "panics"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -2,9 +2,7 @@
/// calculate the average speed of the journey.
fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: Panic with a custom message if `time_elapsed` is 0
if time_elapsed == 0 {
panic!("The journey took no time at all, that's impossible!");
}
(end - start) / time_elapsed
}
@ -21,7 +19,7 @@ mod tests {
// 👇 With the `#[should_panic]` annotation we can assert that we expect the code
// under test to panic. We can also check the panic message by using `expected`.
// This is all part of Rust's built-in test framework!
#[should_panic(expected = "The journey took no time at all, that's impossible!")]
#[should_panic(expected = "The journey took no time at all. That's impossible!")]
fn by_zero() {
speed(0, 10, 0);
}

View File

@ -2,3 +2,9 @@
name = "factorial"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -9,13 +9,6 @@
// `factorial(2)` to return `2`, and so on.
//
// Use only what you learned! No loops yet, so you'll have to use recursion!
fn factorial(n: u16) -> u16 {
if n == 0 {
1
} else {
n * factorial(n - 1)
}
}
#[cfg(test)]
mod tests {

View File

@ -2,3 +2,9 @@
name = "while_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -4,13 +4,7 @@ pub fn factorial(n: u32) -> u32 {
// interprets as "I'll get back to this later", thus
// suppressing type errors.
// It panics at runtime.
let mut result: u32 = 1; // base case
let mut i: u32 = 1;
while i <= n {
result *= i;
i += 1;
}
result
todo!()
}
#[cfg(test)]

View File

@ -2,3 +2,9 @@
name = "for_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -1,10 +1,6 @@
// Rewrite the factorial function using a `for` loop.
pub fn factorial(n: u32) -> u32 {
let mut result: u32 = 1; // base case
for i in 2..=n {
result *= i;
}
result
todo!()
}
#[cfg(test)]

View File

@ -2,3 +2,9 @@
name = "overflow"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -2,3 +2,9 @@
name = "saturating"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -1,9 +1,9 @@
pub fn factorial(n: u32) -> u32 {
let mut result: u32 = 1;
let mut result = 1;
for i in 1..=n {
// Use saturating multiplication to stop at the maximum value of u32
// rather than overflowing and wrapping around
result = result.saturating_mul(i);
result *= i;
}
result
}

View File

@ -2,3 +2,9 @@
name = "as_cast"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -6,7 +6,7 @@ mod tests {
#[test]
fn u16_to_u32() {
let v: u32 = 47;
let v: u32 = todo!();
assert_eq!(47u16 as u32, v);
}
@ -23,15 +23,15 @@ mod tests {
// You could solve this by using exactly the same expression as above,
// but that would defeat the purpose of the exercise. Instead, use a genuine
// `i8` value that is equivalent to `255` when converted from `u8`.
let y: i8 = -1;
// `i8` value that is equivalent to `255` when converted to `u8`.
let y: i8 = todo!();
assert_eq!(x, y);
}
#[test]
fn bool_to_u8() {
let v: u8 = 1;
let v: u8 = todo!();
assert_eq!(true as u8, v);
}
}

View File

@ -2,3 +2,9 @@
name = "intro_02"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -1,6 +1,6 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to start modelling a software ticket!"
"I'm ready to __!"
}
#[cfg(test)]

View File

@ -2,3 +2,9 @@
name = "struct_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -4,16 +4,6 @@
//
// It should also have a method named `is_available` that returns a `true` if the quantity is
// greater than 0, otherwise `false`.
struct Order {
price: u8,
quantity: u8,
}
impl Order {
fn is_available(self) -> bool {
self.quantity > 0
}
}
#[cfg(test)]
mod tests {

View File

@ -5,3 +5,9 @@ edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -12,27 +12,13 @@ impl Ticket {
// - the `title` should be at most 50 bytes long.
// - the `description` should be at most 500 bytes long.
// The method should panic if any of the requirements are not met.
// You can find the needed panic messages in the tests.
//
// You'll have to use what you learned in the previous exercises,
// as well as some `String` methods. Use the documentation of Rust's standard library
// to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html
fn new(title: String, description: String, status: String) -> Self {
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed")
}
if title.is_empty() {
panic!("Title cannot be empty")
}
if description.is_empty() {
panic!("Description cannot be empty")
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes")
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes")
}
todo!();
Self {
title,
description,

View File

@ -2,3 +2,9 @@
name = "modules"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@ -1,7 +1,6 @@
mod helpers {
// TODO: Make this code compile, either by adding a `use` statement or by using
// the appropriate path to refer to the `Ticket` struct.
use super::Ticket;
fn create_todo_ticket(title: String, description: String) -> Ticket {
Ticket::new(title, description, "To-Do".into())

View File

@ -1,12 +1,12 @@
mod ticket {
pub struct Ticket {
struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
@ -48,14 +48,14 @@ mod tests {
// You should be seeing this error when trying to run this exercise:
//
// error[E0616]: field `description` of struct `encapsulation::ticket::Ticket` is private
// error[E0616]: field `description` of struct `Ticket` is private
// |
// | assert_eq!(ticket.description, "A description");
// | ^^^^^^^^^^^^^^^^^^
//
// TODO: Once you have verified that the below does not compile,
// comment the line out to move on to the next exercise!
// assert_eq!(ticket.description, "A description");
assert_eq!(ticket.description, "A description");
}
fn encapsulation_cannot_be_violated() {
@ -68,10 +68,10 @@ mod tests {
//
// TODO: Once you have verified that the below does not compile,
// comment the lines out to move on to the next exercise!
// let ticket = Ticket {
// title: "A title".into(),
// description: "A description".into(),
// status: "To-Do".into(),
// };
let ticket = Ticket {
title: "A title".into(),
description: "A description".into(),
status: "To-Do".into(),
};
}
}

View File

@ -34,17 +34,6 @@ pub mod ticket {
// - `title` that returns the `title` field.
// - `description` that returns the `description` field.
// - `status` that returns the `status` field.
pub fn title(self) -> String {
self.title
}
pub fn description(self) -> String {
self.description
}
pub fn status(self) -> String {
self.status
}
}
}

View File

@ -1,6 +1,6 @@
// TODO: based on what we just learned about ownership, it sounds like immutable references
// are a good fit for our accessor methods.
// Change the existing implementation of `Ticket`'s accessor methods take a reference
// Change the existing implementation of `Ticket`'s accessor methods to take a reference
// to `self` as an argument, rather than taking ownership of it.
pub struct Ticket {
@ -34,16 +34,16 @@ impl Ticket {
}
}
pub fn title(&self) -> &String {
&self.title
pub fn title(self) -> String {
self.title
}
pub fn description(&self) -> &String {
&self.description
pub fn description(self) -> String {
self.description
}
pub fn status(&self) -> &String {
&self.status
pub fn status(self) -> String {
self.status
}
}

View File

@ -11,9 +11,21 @@ pub struct Ticket {
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
validate_title(&title);
validate_description(&description);
validate_status(&status);
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket {
title,
@ -33,45 +45,6 @@ impl Ticket {
pub fn status(&self) -> &String {
&self.status
}
pub fn set_title(&mut self, title: String) {
validate_title(&title);
self.title = title;
}
pub fn set_description(&mut self, description: String) {
validate_description(&description);
self.description = description;
}
pub fn set_status(&mut self, status: String) {
validate_status(&status);
self.status = status;
}
}
fn validate_title(title: &String) {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
}
fn validate_description(description: &String) {
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
}
fn validate_status(status: &String) {
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
}
#[cfg(test)]

View File

@ -6,16 +6,16 @@ mod tests {
#[test]
fn u16_size() {
assert_eq!(size_of::<u16>(), 2);
assert_eq!(size_of::<u16>(), todo!());
}
#[test]
fn i32_size() {
assert_eq!(size_of::<i32>(), 4);
assert_eq!(size_of::<i32>(), todo!());
}
#[test]
fn bool_size() {
assert_eq!(size_of::<bool>(), 1);
assert_eq!(size_of::<bool>(), todo!());
}
}

View File

@ -13,7 +13,7 @@ mod tests {
#[test]
fn string_size() {
assert_eq!(size_of::<String>(), 24);
assert_eq!(size_of::<String>(), todo!());
}
#[test]
@ -21,8 +21,8 @@ mod tests {
// This is a tricky question!
// The "intuitive" answer happens to be the correct answer this time,
// but, in general, the memory layout of structs is a more complex topic.
// If you're curious, check out the "Data layout" section of the Rustonomicon
// https://doc.rust-lang.org/nomicon/data.html for more information.
assert_eq!(size_of::<Ticket>(), 72);
// If you're curious, check out the "Type layout" section of The Rust Reference
// https://doc.rust-lang.org/reference/type-layout.html for more information.
assert_eq!(size_of::<Ticket>(), todo!());
}
}

View File

@ -13,16 +13,16 @@ mod tests {
#[test]
fn u16_ref_size() {
assert_eq!(size_of::<&u16>(), 8);
assert_eq!(size_of::<&u16>(), todo!());
}
#[test]
fn u64_mut_ref_size() {
assert_eq!(size_of::<&mut u64>(), 8);
assert_eq!(size_of::<&mut u64>(), todo!());
}
#[test]
fn ticket_ref_size() {
assert_eq!(size_of::<&Ticket>(), 8);
assert_eq!(size_of::<&Ticket>(), todo!());
}
}

View File

@ -2,7 +2,7 @@
// We'll pick the concept up again in a later chapter after covering traits and
// interior mutability.
fn outro() -> &'static str {
"I have a basic understanding of destructors!"
"I have a basic understanding of __!"
}
#[cfg(test)]

View File

@ -11,74 +11,3 @@
// Integration here has a very specific meaning: they test **the public API** of your project.
// You'll need to pay attention to the visibility of your types and methods; integration
// tests can't access private or `pub(crate)` items.
pub struct Order {
product_name: String,
quantity: u32,
unit_price: u32,
}
impl Order {
pub fn new(product_name: String, quantity: u32, unit_price: u32) -> Order {
validate_product_name(&product_name);
validate_quantity(&quantity);
validate_unit_price(&unit_price);
Order {
product_name,
quantity,
unit_price,
}
}
pub fn product_name(&self) -> &String {
&self.product_name
}
pub fn quantity(&self) -> &u32 {
&self.quantity
}
pub fn unit_price(&self) -> &u32 {
&self.unit_price
}
pub fn set_product_name(&mut self, product_name: String) {
validate_product_name(&product_name);
self.product_name = product_name;
}
pub fn set_quantity(&mut self, quantity: u32) {
validate_quantity(&quantity);
self.quantity = quantity;
}
pub fn set_unit_price(&mut self, unit_price: u32) {
validate_unit_price(&unit_price);
self.unit_price = unit_price;
}
pub fn total(&self) -> u32 {
self.quantity * self.unit_price
}
}
fn validate_product_name(product_name: &String) {
if product_name.is_empty() {
panic!("Product name cannot be empty");
}
if product_name.len() > 300 {
panic!("Product name cannot be longer than 300 bytes");
}
}
fn validate_quantity(quantity: &u32) {
if quantity == &0 {
panic!("Quantity must be greater than zero");
}
}
fn validate_unit_price(unit_price: &u32) {
if unit_price == &0 {
panic!("Unit price must be greater than zero");
}
}

View File

@ -1,6 +1,6 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to learn about traits!"
"I'm ready to __!"
}
#[cfg(test)]

View File

@ -3,22 +3,6 @@
//
// Then implement the trait for `u32` and `i32`.
pub trait IsEven {
fn is_even(self) -> bool;
}
impl IsEven for u32 {
fn is_even(self) -> bool {
self % 2 == 0
}
}
impl IsEven for i32 {
fn is_even(self) -> bool {
self % 2 == 0
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -3,3 +3,9 @@
// a foreign type (`u32`, from `std`).
// Look at the compiler error to get familiar with what it looks like.
// Then delete the code below and move on to the next exercise.
impl PartialEq for u32 {
fn eq(&self, _other: &Self) -> bool {
todo!()
}
}

View File

@ -8,13 +8,7 @@ struct Ticket {
// TODO: Implement the `PartialEq` trait for `Ticket`.
impl PartialEq for Ticket {
fn eq(&self, other: &Self) -> bool {
self.title == other.title
&& self.description == other.description
&& self.status == other.status
}
}
impl PartialEq for Ticket {}
#[cfg(test)]
mod tests {

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