Compare commits

..

63 Commits

Author SHA1 Message Date
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
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
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
90 changed files with 1698 additions and 545 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,83 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/checkout@v4
with:
fetch-depth: 0
repository: mainmatter/core-sans-a-fonts
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
path: core-sans-a-fonts
- name: Install Fonts
run: |
sudo cp -r core-sans-a-fonts/* /usr/local/share/fonts/
sudo fc-cache -f -v
fc-list | grep "Core Sans"
- 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 calibre pdftk
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 +104,37 @@ 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
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,129 @@
[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.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 = ["Noto Color Emoji:mode=harf"]
sansfontfallback = ["Noto Color Emoji:mode=harf"]
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 = ["Noto Color Emoji:mode=harf"]
sansfontfallback = ["Noto Color Emoji:mode=harf"]
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 = ["Noto Color Emoji:mode=harf"]
sansfontfallback = ["Noto Color Emoji:mode=harf"]
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
}
}
}

190
book/link2alias.json Normal file
View File

@@ -0,0 +1,190 @@
{
"https://blog.acolyer.org/2019/05/28/cheri-abi/": "f2u",
"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://marabos.nl/atomics/": "fxg",
"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://tokio.rs/tokio/tutorial/select": "f6n",
"https://valgrind.org/docs/manual/dh-manual.html": "f2t",
"https://veykril.github.io/tlborm/": "fz5",
"https://without.boats/blog/the-scoped-task-trilemma/": "f67",
"https://www.amazon.com/dp/B0DJ14KQQG/": "f6g",
"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.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

@@ -32,6 +32,11 @@ a mentor to help you along the way should you get stuck. You can
also find solutions to 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

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

@@ -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,7 +81,7 @@ 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`
@@ -112,3 +112,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

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

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

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

@@ -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,12 +27,12 @@ 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.

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

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

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

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

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

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

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

@@ -1,6 +1,6 @@
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
a + b * 4u8
}
#[cfg(test)]

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

@@ -1,6 +1,6 @@
/// Return `true` if `n` is even, `false` otherwise.
fn is_even(n: u32) -> bool {
n % 2 == 0
todo!()
}
#[cfg(test)]

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
}

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

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

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

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

@@ -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);
}
@@ -24,14 +24,14 @@ 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;
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

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

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

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

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

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

View File

@@ -8,7 +8,7 @@
// print both sides of the comparison to the terminal.
// If the compared type doesn't implement `Debug`, it doesn't know how to represent them!
#[derive(Debug, PartialEq)]
#[derive(PartialEq)]
struct Ticket {
title: String,
description: String,

View File

@@ -6,10 +6,7 @@
// collections (e.g. BTreeMap).
/// Return the minimum of two values.
pub fn min<T>(left: T, right: T) -> T
where
T: Ord,
{
pub fn min<T>(left: T, right: T) -> T {
if left <= right {
left
} else {

View File

@@ -6,11 +6,12 @@ fn test_saturating_u16() {
let b: SaturatingU16 = 5u8.into();
let c: SaturatingU16 = u16::MAX.into();
let d: SaturatingU16 = (&1u16).into();
let e = &c;
assert_eq!(a + b, SaturatingU16::from(15u16));
assert_eq!(a + c, SaturatingU16::from(u16::MAX));
assert_eq!(a + d, SaturatingU16::from(11u16));
assert_eq!(a + a, 20u16);
assert_eq!(a + 5u16, 15u16);
assert_eq!(a + &u16::MAX, SaturatingU16::from(u16::MAX));
assert_eq!(a + e, SaturatingU16::from(u16::MAX));
}

View File

@@ -1,5 +1,5 @@
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `TicketTitle` type,
// enforcing that the title is not empty and is not longer than 50 characters.
// enforcing that the title is not empty and is not longer than 50 bytes.
// Implement the traits required to make the tests pass too.
pub struct TicketTitle(String);

View File

@@ -43,7 +43,7 @@ mod tests {
}
#[test]
fn thirthieth() {
fn thirtieth() {
assert_eq!(fibonacci(30), 832040);
}
}

View File

@@ -1,6 +1,5 @@
// TODO: Define a function named `lowercase` that converts all characters in a string to lowercase,
// modifying the input in place.
// Does it need to take a `&mut String`? Does a `&mut [str]` work? Why or why not?
// TODO: Define a function named `squared` that raises all `i32`s within a slice to the power of 2.
// The slice should be modified in place.
#[cfg(test)]
mod tests {
@@ -8,29 +7,22 @@ mod tests {
#[test]
fn empty() {
let mut s = String::from("");
lowercase(&mut s);
assert_eq!(s, "");
let mut s = vec![];
squared(&mut s);
assert_eq!(s, vec![]);
}
#[test]
fn one_char() {
let mut s = String::from("A");
lowercase(&mut s);
assert_eq!(s, "a");
fn one() {
let mut s = [2];
squared(&mut s);
assert_eq!(s, [4]);
}
#[test]
fn multiple_chars() {
let mut s = String::from("Hello, World!");
lowercase(&mut s);
assert_eq!(s, "hello, world!");
}
#[test]
fn mut_slice() {
let mut s = "Hello, World!".to_string();
lowercase(s.as_mut_str());
assert_eq!(s, "hello, world!");
fn multiple() {
let mut s = vec![2, 4];
squared(&mut s);
assert_eq!(s, vec![4, 16]);
}
}

View File

@@ -99,5 +99,11 @@ mod tests {
let ticket = &store[id];
assert_eq!(ticket.status, Status::InProgress);
let ticket = &mut store[&id];
ticket.status = Status::Done;
let ticket = &store[id];
assert_eq!(ticket.status, Status::Done);
}
}

View File

@@ -7,7 +7,7 @@ pub enum Command {
Insert(todo!()),
}
// Start the system by spawning the server the thread.
// Start the system by spawning the server thread.
// It returns a `Sender` instance which can then be used
// by one or more clients to interact with the server.
pub fn launch() -> Sender<Command> {

View File

@@ -38,7 +38,7 @@ enum Command {
},
}
pub fn server(receiver: Receiver<Command>) {
fn server(receiver: Receiver<Command>) {
let mut store = TicketStore::new();
loop {
match receiver.recv() {

View File

@@ -1,5 +1,5 @@
// TODO: Implement the `fixed_reply` function. It should accept two `TcpListener` instances,
// accept connections on both of them concurrently, and always reply clients by sending
// accept connections on both of them concurrently, and always reply to clients by sending
// the `Display` representation of the `reply` argument as a response.
use std::fmt::Display;
use tokio::io::AsyncWriteExt;

12
helpers/json2redirects.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Ensure the JSON file is provided as an argument
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <input_json_file>"
exit 1
fi
input_file=$1
# Use jq to parse the JSON and format the output
jq -r 'to_entries[] | "/" + .value + " " + .key' "$input_file"

View File

@@ -3,7 +3,6 @@ use mdbook::book::Book;
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use mdbook::BookItem;
/// A no-op preprocessor.
pub struct ExerciseLinker;
impl ExerciseLinker {
@@ -33,7 +32,7 @@ impl Preprocessor for ExerciseLinker {
book.sections
.iter_mut()
.for_each(|i| process_book_item(i, &root_url));
.for_each(|i| process_book_item(i, &ctx.renderer, &root_url));
Ok(book)
}
@@ -42,11 +41,11 @@ impl Preprocessor for ExerciseLinker {
}
}
fn process_book_item(item: &mut BookItem, root_url: &str) {
fn process_book_item(item: &mut BookItem, renderer: &str, root_url: &str) {
match item {
BookItem::Chapter(chapter) => {
chapter.sub_items.iter_mut().for_each(|item| {
process_book_item(item, root_url);
process_book_item(item, renderer, root_url);
});
let Some(source_path) = &chapter.source_path else {
@@ -61,10 +60,14 @@ fn process_book_item(item: &mut BookItem, root_url: &str) {
let exercise_path = source_path.strip_suffix(".md").unwrap();
let link_section = format!(
"\n## Exercise\n\nThe exercise for this section is located in [`{exercise_path}`]({})",
"\n## Exercise\n\nThe exercise for this section is located in [`{exercise_path}`]({})\n",
format!("{}/{}", root_url, exercise_path)
);
chapter.content.push_str(&link_section);
if renderer == "pandoc" {
chapter.content.push_str("`\\newpage`{=latex}\n");
}
}
BookItem::Separator => {}
BookItem::PartTitle(_) => {}

View File

@@ -0,0 +1,15 @@
[package]
name = "mdbook-link-shortener"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.86"
bimap = { version = "0.6.3", features = ["serde"] }
clap = { version = "4.5.4", features = ["derive"] }
itertools = "0.13.0"
mdbook = "0.4.40"
pulldown-cmark = "0.11.0"
pulldown-cmark-to-cmark = "15"
semver = "1.0.23"
serde_json = "1.0.117"

View File

@@ -0,0 +1,224 @@
use anyhow::{Context, Error};
use bimap::BiHashMap;
use itertools::Itertools;
use mdbook::book::{Book, Chapter};
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use mdbook::BookItem;
use std::collections::{BTreeMap, BTreeSet};
use std::fs::File;
use std::path::PathBuf;
use std::str::FromStr;
pub struct LinkShortener;
struct AliasGenerator {
cursors: [usize; 3],
}
impl AliasGenerator {
const ALPHABET: &'static [u8] = b"f2z4x6v8bnm3q5w7e9rtyuplkshgjdca";
fn new() -> AliasGenerator {
AliasGenerator { cursors: [0, 0, 0] }
}
/// Generate a 4 alphanumeric long alias, starting from "aaaa" and incrementing by one each time
/// until "9999", using only lowercase letters and numbers.
/// We skip ambiguous characters like "0", "o", "1", "l".
fn next(&mut self) -> String {
let mut alias = String::with_capacity(4);
for cursor in &mut self.cursors {
alias.push(Self::ALPHABET[*cursor] as char);
}
for cursor in self.cursors.iter_mut().rev() {
if *cursor == Self::ALPHABET.len() - 1 {
*cursor = 0;
} else {
*cursor += 1;
break;
}
}
alias
}
/// Generate a unique alias that is not already used by the `link2alias` map.
fn next_until_unique(&mut self, link2alias: &BiHashMap<String, String>) -> String {
let mut alias = self.next();
while link2alias.contains_right(&alias) {
alias = self.next();
}
alias
}
}
impl LinkShortener {
pub fn new() -> LinkShortener {
LinkShortener
}
}
impl Preprocessor for LinkShortener {
fn name(&self) -> &str {
"link-shortener"
}
fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book, Error> {
let config = ctx
.config
.get_preprocessor(self.name())
.context("Failed to get preprocessor configuration")?;
let root_url = {
let root_url = config.get("base_url").context("Failed to get `base_url`")?;
root_url
.as_str()
.context("`base_url` is not a string")?
.to_owned()
};
let mapping = {
let mapping = config.get("mapping").context("Failed to get `mapping`")?;
let mapping = mapping
.as_str()
.context("`mapping` is not a string")?
.to_owned();
PathBuf::from_str(&mapping).context("Failed to parse `mapping` as a path")?
};
let mut link2alias = {
match File::open(&mapping) {
Ok(file) => {
serde_json::from_reader(file).context("Failed to parse existing mapping")?
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
BiHashMap::new()
} else {
return Err(e).context("Failed to open existing mapping");
}
}
}
};
let verify = config
.get("verify")
.context("Failed to get `verify`")?
.as_bool()
.context("`verify` is not a boolean")?;
// Env var overrides config
let verify = std::env::var("LINK_SHORTENER_VERIFY")
.map(|v| v == "true")
.unwrap_or(verify);
let mut alias_gen = AliasGenerator::new();
book.sections.iter_mut().for_each(|i| {
if let BookItem::Chapter(c) = i {
c.content = replace_anchors(c, &root_url, &mut alias_gen, &mut link2alias, verify)
.expect("Error converting links for chapter");
for i in c.sub_items.iter_mut() {
if let BookItem::Chapter(sub_chapter) = i {
sub_chapter.content = replace_anchors(
sub_chapter,
&root_url,
&mut alias_gen,
&mut link2alias,
verify,
)
.expect("Error converting links for subchapter");
}
}
}
});
if !verify {
std::fs::create_dir_all(mapping.parent().expect("Mapping file path has no parent"))?;
let mut file = File::create(&mapping).context("Failed to upsert mapping file")?;
let ordered = link2alias.iter().collect::<BTreeMap<_, _>>();
serde_json::to_writer_pretty(&mut file, &ordered)?;
}
Ok(book)
}
fn supports_renderer(&self, _renderer: &str) -> bool {
true
}
}
fn replace_anchors(
chapter: &mut Chapter,
root_url: &str,
alias_gen: &mut AliasGenerator,
link2alias: &mut BiHashMap<String, String>,
verify: bool,
) -> Result<String, anyhow::Error> {
use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag};
use pulldown_cmark_to_cmark::cmark;
let mut buf = String::with_capacity(chapter.content.len());
let mut unshortened_links = BTreeSet::new();
let events = Parser::new_ext(&chapter.content, Options::all())
.map(|e| {
let Event::Start(Tag::Link {
link_type,
dest_url,
title,
id,
}) = &e
else {
return e;
};
match link_type {
LinkType::Autolink
| LinkType::Shortcut
| LinkType::Inline
| LinkType::Reference
| LinkType::Collapsed => {
if dest_url.starts_with("http") {
let alias = if let Some(alias) = link2alias.get_by_left(dest_url.as_ref()) {
alias.to_owned()
} else {
if verify {
unshortened_links.insert(dest_url.to_string());
return e;
}
let alias = alias_gen.next_until_unique(&link2alias);
alias
};
link2alias.insert(dest_url.to_string(), alias.clone());
Event::Start(Tag::Link {
link_type: link_type.to_owned(),
dest_url: CowStr::from(format!(
"{root_url}/{alias}",
root_url = root_url,
alias = alias
)),
title: title.clone(),
id: id.clone(),
})
} else {
e
}
}
LinkType::Email
| LinkType::ReferenceUnknown
| LinkType::CollapsedUnknown
| LinkType::ShortcutUnknown => e,
}
})
.collect_vec();
if verify && !unshortened_links.is_empty() {
let unshortened_links = unshortened_links.iter().join(", ");
return Err(anyhow::anyhow!(
"The following links are not shortened: {unshortened_links}\nRun again with `LINK_SHORTENER_VERIFY=false` to update the mapping \
with the shortened links."
));
}
cmark(events.into_iter(), &mut buf)
.map(|_| buf)
.map_err(|err| anyhow::anyhow!("Markdown serialization failed: {err}"))
}

View File

@@ -0,0 +1,66 @@
use clap::Parser;
use mdbook::errors::Error;
use mdbook::preprocess::{CmdPreprocessor, Preprocessor};
use mdbook_link_shortener::LinkShortener;
use semver::{Version, VersionReq};
use std::io;
use std::process;
#[derive(clap::Parser, Debug)]
#[command(version, about)]
pub struct Cli {
#[command(subcommand)]
sub: Option<SubCommand>,
}
#[derive(clap::Parser, Debug)]
pub enum SubCommand {
#[clap(name = "supports")]
Supports(Supports),
}
#[derive(clap::Parser, Debug)]
pub struct Supports {
#[arg(long)]
renderer: String,
}
fn main() -> Result<(), anyhow::Error> {
let cli = Cli::parse();
let preprocessor = LinkShortener::new();
if let Some(SubCommand::Supports(Supports { renderer })) = cli.sub {
let code = if preprocessor.supports_renderer(&renderer) {
0
} else {
1
};
process::exit(code);
}
handle_preprocessing(&preprocessor)?;
Ok(())
}
fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> {
let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
let book_version = Version::parse(&ctx.mdbook_version)?;
let version_req = VersionReq::parse(mdbook::MDBOOK_VERSION)?;
if !version_req.matches(&book_version) {
eprintln!(
"Warning: The {} plugin was built against version {} of mdbook, \
but we're being called from version {}",
pre.name(),
mdbook::MDBOOK_VERSION,
ctx.mdbook_version
);
}
let processed_book = pre.run(&ctx, book)?;
serde_json::to_writer(io::stdout(), &processed_book)?;
Ok(())
}

188
site/_redirects Normal file
View File

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