Compare commits

...

95 Commits

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

* Fix installs.

* Go the apt-get route

* Another attempt

* Avoid installing twice.

* Re-order.

* Add more packages.

* Minimise deps. Fix link checker.

* Missing package.

* Missing package.

* Missing package.

* More packages.

* Missing package.

* Missing package.

* More packages...

* Remove.

* Fix link checker.

* Fix link checker.

* Fix path.

* Add subtitle.

* Avoid running over the right margin.

* Avoid running over the right margin.

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

`if the producers enqueues messages` -> `if the producers enqueue messages`
2024-08-05 15:03:31 +02:00
95 changed files with 2807 additions and 919 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,30 +19,149 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get Core Sans
uses: actions/checkout@v4
if: "!github.event.pull_request.head.repo.fork"
with:
fetch-depth: 0
repository: mainmatter/core-sans-a-fonts
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
path: core-sans-a-fonts
- name: Install Core Sans Font
if: "!github.event.pull_request.head.repo.fork"
run: |
sudo cp -r core-sans-a-fonts/* /usr/local/share/fonts/
sudo fc-cache -f -v
fc-list | grep "Core Sans"
- name: Use Fallback font for fork PRs
if: "github.event.pull_request.head.repo.fork"
run: |
sed -i 's/"BoldFont=CoreSansA65.ttf",//g' book/book.toml
sed -i 's/"ItalicFont=CoreSansA45It.ttf",//g' book/book.toml
sed -i 's/"BoldItalicFont=CoreSansA65It.ttf",//g' book/book.toml
sed -i 's/CoreSansA45.ttf/Open Sans:style=Regular/g' book/book.toml
- uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install plugin
- name: Install exercise plugin
run: cargo install --path helpers/mdbook-exercise-linker
- name: Install link shortener plugin
run: cargo install --path helpers/mdbook-link-shortener
- name: Install mdbook-pandoc, calibre, pdftk and related dependencies
run: |
cargo install mdbook-pandoc --locked --version 0.7.1
sudo apt-get update
sudo apt-get install -y fonts-noto fonts-open-sans calibre pdftk
sudo fc-cache -f -v
export PANDOC_VERSION=3.3
curl -LsSf https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-linux-amd64.tar.gz | tar zxf -
echo "$PWD/pandoc-${PANDOC_VERSION}/bin" >> $GITHUB_PATH
shell: bash
- name: Setup TeX Live
uses: TeX-Live/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:
fail: true
args: |
--exclude-loopback
--require-https
--no-progress
book/book
# Upload the book as an artifact
--exclude-loopback
--require-https
--no-progress
book/book/html/
# Upload the HTML book as an artifact
- uses: actions/upload-artifact@v4
with:
name: book
path: book/book
# When you support multiple formats, the output directory changes
# to include the format in its path.
path: book/book/html
- uses: actions/upload-artifact@v4
with:
name: online-pdf
path: book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf
- uses: actions/upload-artifact@v4
with:
name: paperback
path: book/book/pandoc/paperback/100-exercises-to-learn-rust.pdf
- uses: actions/upload-artifact@v4
with:
name: ePUB
path: book/book/pandoc/html/100-exercises-to-learn-rust.epub
is_fresh:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: sudo apt-get update && sudo apt-get install -y jq
- run: |
./helpers/json2redirects.sh book/link2alias.json > site/_redirects
# Verify nothing has changed, meaning that the redirect file is up-to-date
- run: |
git diff --exit-code site/_redirects
gravity:
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/download-artifact@v4
with:
path: book
pattern: online-pdf
- uses: pnpm/action-setup@v4
with:
version: 9
- run: ls -las ./book
- name: Run Gravity
run: pnpm dlx @gravityci/cli "./book/**/*"
env:
GRAVITY_TOKEN: ${{ secrets.GRAVITY_TOKEN }}
formatter:
runs-on: ubuntu-latest

2022
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,11 @@
[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"
# This is needed to guarantee the expected behaviour on that specific exercise,

BIN
book/assets/back.pdf Normal file

Binary file not shown.

BIN
book/assets/cover.pdf Normal file

Binary file not shown.

View File

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

211
book/custom.theme Normal file
View File

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

196
book/link2alias.json Normal file
View File

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

4
book/metadata.yml Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -81,10 +81,13 @@ You have to use a **path** pointing to the entity you want to access.
You can compose the path in various ways:
- starting from the root of the current crate, e.g. `crate::module_1::module_2::MyStruct`
- starting from the root of the current crate, e.g. `crate::module_1::MyStruct`
- starting from the parent module, e.g. `super::my_function`
- starting from the current module, e.g. `sub_module_1::MyStruct`
Both `crate` and `super` are **keywords**.\
`crate` refers to the root of the current crate, while `super` refers to the parent of the current module.
Having to write the full path every time you want to refer to a type can be cumbersome.
To make your life easier, you can introduce a `use` statement to bring the entity into scope.

View File

@@ -30,7 +30,7 @@ instantiation syntax:
// This won't work!
let ticket = Ticket {
title: "Build a ticket system".into(),
description: "Create a system that can manage tickets across a Kanban board".into(),
description: "A Kanban board".into(),
status: "Open".into()
};
```

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,11 @@
Let's go back to where our string journey started:
```rust
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
let ticket = Ticket::new(
"A title".into(),
"A description".into(),
"To-Do".into()
);
```
We now know enough to start unpacking what `.into()` is doing here.
@@ -14,7 +18,11 @@ This is the signature of the `new` method:
```rust
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Self {
pub fn new(
title: String,
description: String,
status: String
) -> Self {
// [...]
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -38,7 +38,7 @@ When the process exits, the operating system will reclaim that memory.
Keeping this in mind, it can be OK to leak memory when:
- The amount of memory you need to leak is not unbounded/known upfront, or
- The amount of memory you need to leak is bounded/known upfront, or
- Your process is short-lived and you're confident you won't exhaust
all the available memory before it exits

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,7 @@ 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`.
// `i8` value that is equivalent to `255` when converted to `u8`.
let y: i8 = todo!();
assert_eq!(x, y);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.59"
thiserror = "1.0.69"
[dev-dependencies]
common = { path = "../../../helpers/common" }

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

@@ -5,6 +5,9 @@ use ticket_fields::{TicketDescription, TicketTitle};
// it contains using a `for` loop.
//
// Hint: you shouldn't have to implement the `Iterator` trait in this case.
// You want to *delegate* the iteration to the `Vec<Ticket>` field in `TicketStore`.
// Look at the standard library documentation for `Vec` to find the right type
// to return from `into_iter`.
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,

View File

@@ -1,6 +1,10 @@
use ticket_fields::{TicketDescription, TicketTitle};
// TODO: Provide an `iter` method that returns an iterator over `&Ticket` items.
//
// Hint: just like in the previous exercise, you want to delegate the iteration to
// the `Vec<Ticket>` field in `TicketStore`. Look at the standard library documentation
// for `Vec` to find the right type to return from `iter`.
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,

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

@@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.59"
thiserror = "1.0.69"
ticket_fields = { path = "../../../helpers/ticket_fields" }

View File

@@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.60"
thiserror = "1.0.69"
ticket_fields = { path = "../../../helpers/ticket_fields" }

View File

@@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.60"
thiserror = "1.0.69"
ticket_fields = { path = "../../../helpers/ticket_fields" }

View File

@@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.83"
anyhow = "1.0.100"
tokio = { version = "1", features = ["full"] }

View File

@@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.83"
anyhow = "1.0.100"
tokio = { version = "1", features = ["full"] }

View File

@@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.83"
anyhow = "1.0.100"
tokio = { version = "1", features = ["full"] }

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;

View File

@@ -4,5 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.83"
anyhow = "1.0.100"
tokio = { version = "1", features = ["full"] }

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

@@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.86"
clap = "4.5.4"
mdbook = "0.4.40"
semver = "1.0.23"
serde_json = "1.0.117"
anyhow = "1.0.100"
clap = "4.5.50"
mdbook = "0.4.52"
semver = "1.0.27"
serde_json = "1.0.145"

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.100"
bimap = { version = "0.6.3", features = ["serde"] }
clap = { version = "4.5.50", features = ["derive"] }
itertools = "0.13.0"
mdbook = "0.4.52"
pulldown-cmark = "0.11.3"
pulldown-cmark-to-cmark = "15"
semver = "1.0.27"
serde_json = "1.0.145"

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(())
}

View File

@@ -5,4 +5,4 @@ edition = "2021"
[dependencies]
common = { path = "../common" }
thiserror = "1.0.59"
thiserror = "1.0.69"

194
site/_redirects Normal file
View File

@@ -0,0 +1,194 @@
/f6c https://code.visualstudio.com
/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/
/fv2 https://mainmatter.github.io/rust-workshop-runner/
/fxg https://marabos.nl/atomics/
/f6a https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer
/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/
/fvf https://ti.to/mainmatter/rust-from-scratch-jan-2025
/f6n https://tokio.rs/tokio/tutorial/select
/f2t https://valgrind.org/docs/manual/dh-manual.html
/fz5 https://veykril.github.io/tlborm/
/f2u https://web.archive.org/web/20240517051950/https://blog.acolyer.org/2019/05/28/cheri-abi/
/f67 https://without.boats/blog/the-scoped-task-trilemma/
/f6g https://www.amazon.com/dp/B0DJ14KQQG/
/f6d https://www.jetbrains.com/rust/
/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/
/f6j https://www.rust-lang.org/tools/install
/f6l https://www.youtube.com/playlist?list=PLqbS7AVVErFirH9armw8yXlE6dacF-A6z
/ff8 https://zero2prod.com