Compare commits
85 Commits
97d13e5c85
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8791feb495 | ||
|
|
a2fe212f44 | ||
|
|
20a2b45e49 | ||
|
|
c3cb1b38f6 | ||
|
|
af4fe9cedb | ||
|
|
21f3427c92 | ||
|
|
b839c770b5 | ||
|
|
fd23b201fe | ||
|
|
13850a6b01 | ||
|
|
a7865baf3c | ||
|
|
0e1c66814d | ||
|
|
2f059fc5ba | ||
|
|
63402ef5c2 | ||
|
|
892c37ead9 | ||
|
|
c745cf8ddd | ||
|
|
3f60de3712 | ||
|
|
36f6375c20 | ||
|
|
fb19005d5f | ||
|
|
cc0092b2c6 | ||
|
|
d5e1c00108 | ||
|
|
60947aaacd | ||
|
|
fe40e6e2d0 | ||
|
|
7fb0910b39 | ||
|
|
468b74ba4d | ||
|
|
b0e1e5a1ec | ||
|
|
93536fbcfd | ||
|
|
1993507236 | ||
|
|
a1a2dc1588 | ||
|
|
51cad6bdfe | ||
|
|
ab1eb5d80c | ||
|
|
fd6a56fd84 | ||
|
|
0419e2e8b2 | ||
|
|
4b02e92691 | ||
|
|
eb2d858807 | ||
|
|
4194ee9998 | ||
|
|
9f5e4a0d76 | ||
|
|
da610ee79b | ||
|
|
f1a0eab4a6 | ||
|
|
3addd64dc2 | ||
|
|
a53e4f86a1 | ||
|
|
5edddf102e | ||
|
|
dab563a329 | ||
|
|
c75ff98c6a | ||
|
|
b60e021ab1 | ||
|
|
9e63a5cdf6 | ||
|
|
f4a7472f30 | ||
|
|
04b89ff26e | ||
|
|
26e78072cd | ||
|
|
c540242c15 | ||
|
|
12fb62d8be | ||
|
|
0bf398a351 | ||
|
|
f14a047e8b | ||
|
|
5e23bee61e | ||
|
|
8488317564 | ||
|
|
bd17a59e8e | ||
|
|
8c3e40033b | ||
|
|
a392371ef0 | ||
|
|
f4cb4b55d0 | ||
|
|
45c32d8e41 | ||
|
|
4ca49fa5fd | ||
|
|
c437f75fcf | ||
|
|
f839fc00f9 | ||
|
|
954dd0ff14 | ||
|
|
33bcaf0a7a | ||
|
|
0aecb5380e | ||
|
|
276bdbbcdc | ||
|
|
a899ecec96 | ||
|
|
a0537f8c51 | ||
|
|
73649001ba | ||
|
|
99ee4b27c5 | ||
|
|
af6c605fe6 | ||
|
|
f613c9a58b | ||
|
|
e9c4f37130 | ||
|
|
e768d9dbd8 | ||
|
|
ea71ac2c0a | ||
|
|
0ca42ba058 | ||
|
|
681903a853 | ||
|
|
6bdcf19f5f | ||
|
|
297d78d750 | ||
|
|
cab4de6433 | ||
|
|
52c21a241d | ||
|
|
982a8f0789 | ||
|
|
1b050774e7 | ||
|
|
d269320825 | ||
|
|
8730a8458a |
60
.github/workflows/ci.yml
vendored
60
.github/workflows/ci.yml
vendored
@@ -7,6 +7,10 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# First day of a month
|
||||
- cron: '0 0 1 * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -15,23 +19,44 @@ 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 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 and related dependencies
|
||||
- name: Install mdbook-pandoc, calibre, pdftk and related dependencies
|
||||
run: |
|
||||
cargo install mdbook-pandoc --locked --version 0.7.1
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y fonts-noto calibre
|
||||
|
||||
sudo apt-get install -y fonts-noto fonts-open-sans calibre pdftk
|
||||
sudo fc-cache -f -v
|
||||
export PANDOC_VERSION=3.3
|
||||
curl -LsSf https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-linux-amd64.tar.gz | tar zxf -
|
||||
echo "$PWD/pandoc-${PANDOC_VERSION}/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- name: Setup TeX Live
|
||||
uses: teatimeguest/setup-texlive-action@v3
|
||||
uses: TeX-Live/setup-texlive-action@v3
|
||||
with:
|
||||
packages:
|
||||
scheme-basic
|
||||
@@ -70,6 +95,10 @@ jobs:
|
||||
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
|
||||
@@ -82,9 +111,9 @@ jobs:
|
||||
with:
|
||||
fail: true
|
||||
args: |
|
||||
--exclude-loopback
|
||||
--require-https
|
||||
--no-progress
|
||||
--exclude-loopback
|
||||
--require-https
|
||||
--no-progress
|
||||
book/book/html/
|
||||
# Upload the HTML book as an artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
@@ -117,6 +146,23 @@ jobs:
|
||||
- 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
|
||||
steps:
|
||||
|
||||
1927
Cargo.lock
generated
1927
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
BIN
book/assets/back.pdf
Normal file
BIN
book/assets/back.pdf
Normal file
Binary file not shown.
BIN
book/assets/cover.pdf
Normal file
BIN
book/assets/cover.pdf
Normal file
Binary file not shown.
@@ -18,36 +18,53 @@ after = ["exercise-linker"]
|
||||
[output.html]
|
||||
git-repository-url = "https://github.com/mainmatter/100-exercises-to-learn-rust"
|
||||
|
||||
[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 = "tango"
|
||||
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+
|
||||
mainfont = "Noto Serif"
|
||||
sansfont = "Noto Sans"
|
||||
monofont = "Noto Sans Mono"
|
||||
mainfontfallback = ["Noto Color Emoji:mode=harf"]
|
||||
sansfontfallback = ["Noto Color Emoji:mode=harf"]
|
||||
mainfontfallback = ["Open Sans:style=Regular"]
|
||||
sansfontfallback = ["Open Sans:style=Regular"]
|
||||
monofontfallback = [
|
||||
"Noto Color Emoji:mode=harf",
|
||||
]
|
||||
linkcolor = "blue"
|
||||
urlcolor = "blue"
|
||||
linkcolor = "Links"
|
||||
urlcolor = "Links"
|
||||
urlstyle = "rm"
|
||||
documentclass = "book"
|
||||
fontsize = "11pt"
|
||||
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}",
|
||||
]
|
||||
@@ -62,20 +79,33 @@ metadata-file = "metadata.yml"
|
||||
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+
|
||||
mainfont = "Noto Serif"
|
||||
sansfont = "Noto Sans"
|
||||
monofont = "Noto Sans Mono"
|
||||
mainfontfallback = ["Noto Color Emoji:mode=harf"]
|
||||
sansfontfallback = ["Noto Color Emoji:mode=harf"]
|
||||
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 = "11pt"
|
||||
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}",
|
||||
]
|
||||
@@ -93,14 +123,10 @@ metadata-file = "metadata.yml"
|
||||
|
||||
[output.pandoc.profile.html.variables]
|
||||
# You can get these fonts here: https://fonts.google.com/selection?query=noto+color+
|
||||
mainfont = "Noto Serif"
|
||||
sansfont = "Noto Sans"
|
||||
monofont = "Noto Sans Mono"
|
||||
mainfontfallback = ["Noto Color Emoji:mode=harf"]
|
||||
sansfontfallback = ["Noto Color Emoji:mode=harf"]
|
||||
mainfontfallback = ["Open Sans:style=Regular"]
|
||||
sansfontfallback = ["Open Sans:style=Regular"]
|
||||
monofontfallback = [
|
||||
"Noto Color Emoji:mode=harf",
|
||||
]
|
||||
linkcolor = "blue"
|
||||
urlcolor = "blue"
|
||||
urlstyle = "rm"
|
||||
|
||||
211
book/custom.theme
Normal file
211
book/custom.theme
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://blog.acolyer.org/2019/05/28/cheri-abi/": "f2u",
|
||||
"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",
|
||||
@@ -163,7 +163,9 @@
|
||||
"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",
|
||||
@@ -177,13 +179,18 @@
|
||||
"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"
|
||||
}
|
||||
@@ -24,18 +24,18 @@ 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/).\
|
||||
You can also [download it as a PDF file](https://rust-exercises.com/100-exercises-to-learn-rust.pdf), for offline reading.
|
||||
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
|
||||
|
||||
@@ -67,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
|
||||
|
||||
@@ -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**.\
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -85,6 +85,9 @@ You can compose the path in various ways:
|
||||
- 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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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**.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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!());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -4,5 +4,5 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.83"
|
||||
anyhow = "1.0.100"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
@@ -4,5 +4,5 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.83"
|
||||
anyhow = "1.0.100"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
@@ -4,5 +4,5 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.83"
|
||||
anyhow = "1.0.100"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
@@ -4,5 +4,5 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.83"
|
||||
anyhow = "1.0.100"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -4,12 +4,12 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
anyhow = "1.0.100"
|
||||
bimap = { version = "0.6.3", features = ["serde"] }
|
||||
clap = { version = "4.5.4", features = ["derive"] }
|
||||
clap = { version = "4.5.50", features = ["derive"] }
|
||||
itertools = "0.13.0"
|
||||
mdbook = "0.4.40"
|
||||
pulldown-cmark = "0.11.0"
|
||||
mdbook = "0.4.52"
|
||||
pulldown-cmark = "0.11.3"
|
||||
pulldown-cmark-to-cmark = "15"
|
||||
semver = "1.0.23"
|
||||
serde_json = "1.0.117"
|
||||
semver = "1.0.27"
|
||||
serde_json = "1.0.145"
|
||||
|
||||
@@ -5,4 +5,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
thiserror = "1.0.59"
|
||||
thiserror = "1.0.69"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/f2u https://blog.acolyer.org/2019/05/28/cheri-abi/
|
||||
/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
|
||||
@@ -162,7 +162,9 @@
|
||||
/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
|
||||
@@ -176,12 +178,17 @@
|
||||
/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
|
||||
|
||||
Reference in New Issue
Block a user