Compare commits

..

31 Commits

Author SHA1 Message Date
7a1ca00837 solution to exercise 04_05 2024-07-13 17:39:46 +02:00
1789d43730 solution to exercise 04_04 2024-07-13 17:30:01 +02:00
604a5a20ae solution to exercise 04_03 2024-07-01 16:30:43 +02:00
0829174983 solution to exercise 04_02 2024-07-01 16:23:02 +02:00
ebe07acd1e solution to exercise 04_01 2024-07-01 16:15:42 +02:00
4a4fc2ea0d solution to exercise 03_12 2024-06-27 16:38:30 +02:00
91e6bf5ae4 solution to exercise 03_11 2024-06-27 16:29:31 +02:00
3f57819e4c solution to exercise 03_10 2024-06-27 16:24:32 +02:00
d54a1e160b solution to exercise 03_09 2024-06-27 16:16:19 +02:00
4913da035f solution to exercise 03_08 2024-06-27 16:08:42 +02:00
1f0823a6a4 solution to exercise 03_07 2024-06-23 18:22:03 +02:00
56075fa40e solution to exercise 03_06 2024-06-23 18:08:59 +02:00
852cd5f0b9 solution to exercise 03_05 2024-06-23 17:58:03 +02:00
1c16ac8c22 solution to exercise 03_04 2024-06-23 17:54:39 +02:00
d3b4c0d653 solution to exercise 03_03 2024-06-19 15:03:51 +02:00
8b78ea3a83 solution to exercise 03_02 2024-06-19 14:56:36 +02:00
0e5cdc6d10 solution to exercise 03_01 2024-06-19 14:46:00 +02:00
1d876eff10 solution to exercise 03_00 2024-06-19 14:41:41 +02:00
dfcb9736b2 solution to exercise 02_10 2024-06-17 11:31:06 +02:00
c1e7c3fadd solution to exercise 02_09 2024-06-17 11:22:25 +02:00
978a99b00d solution to exercise 02_08 2024-06-17 11:15:21 +02:00
34189d212e solution to exercise 02_07 2024-06-17 10:59:03 +02:00
9d1869fc9c solution to exercise 02_06 2024-06-17 10:53:22 +02:00
3e56cf1287 solution to exercise 02_05 2024-06-15 21:01:03 +02:00
f91cc0089a solution to exercise 02_04 2024-06-13 17:04:21 +02:00
211f23cea4 solution to exercise 02_03 2024-06-13 16:55:06 +02:00
1e516bf0a5 solution to exercise 02_02 2024-06-13 16:46:44 +02:00
03a0a77394 solution to exercise 02_01 2024-06-13 16:40:40 +02:00
bb27bfad41 solution to exercise 02_00 2024-06-13 16:33:07 +02:00
4d12facc2f solution to exercise 01 2024-06-12 17:12:56 +02:00
1251a6c1b1 solution to exercise 00 2024-06-12 17:06:43 +02:00
118 changed files with 581 additions and 1960 deletions

View File

@ -7,10 +7,6 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
workflow_dispatch:
schedule:
# First day of a month
- cron: '0 0 1 * *'
jobs: jobs:
build: build:
@ -19,93 +15,16 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 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 - uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install exercise plugin - name: Install plugin
run: cargo install --path helpers/mdbook-exercise-linker run: cargo install --path helpers/mdbook-exercise-linker
- name: Install link shortener plugin
run: cargo install --path helpers/mdbook-link-shortener
- name: Install mdbook-pandoc, calibre, pdftk and related dependencies
run: |
cargo install mdbook-pandoc --locked --version 0.7.1
sudo apt-get update
sudo apt-get install -y fonts-noto fonts-open-sans calibre pdftk
sudo fc-cache -f -v
export PANDOC_VERSION=3.3
curl -LsSf https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-linux-amd64.tar.gz | tar zxf -
echo "$PWD/pandoc-${PANDOC_VERSION}/bin" >> $GITHUB_PATH
shell: bash
- name: Setup TeX Live
uses: teatimeguest/setup-texlive-action@v3
with:
packages:
scheme-basic
luatex
lualatex-math
luacolor
luatexbase
luaotfload
framed
unicode-math
xcolor
geometry
longtable
booktabs
array
lua-ul
etoolbox
fancyvrb
footnote
selnolig
natbib
csquotes
bookmark
xurl
amsmath
setspace
iftex
- name: Check `tlmgr` version
run: tlmgr --version
- uses: taiki-e/install-action@v2 - uses: taiki-e/install-action@v2
with: with:
tool: mdbook tool: mdbook
- name: Build book - name: Build book
env:
LINK_SHORTENER_VERIFY: "true"
run: | run: |
cd book cd book
mdbook build 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 - name: Link Checker
uses: lycheeverse/lychee-action@v1 uses: lycheeverse/lychee-action@v1
with: with:
@ -114,54 +33,24 @@ jobs:
--exclude-loopback --exclude-loopback
--require-https --require-https
--no-progress --no-progress
book/book/html/ book/book
# Upload the HTML book as an artifact # Upload the book as an artifact
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: book name: book
# When you support multiple formats, the output directory changes path: book/book
# to include the format in its path. # Commit and push all changed files.
path: book/book/html # Must only affect files that are listed in "paths-ignore".
- uses: actions/upload-artifact@v4 - name: Git commit build artifacts
with: # Only run on main branch push (e.g. pull request merge).
name: online-pdf if: github.event_name == 'push'
path: book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf run: |
- uses: actions/upload-artifact@v4 git checkout -b deploy
with: git config --global user.name "Deployer"
name: paperback git config --global user.email "username@users.noreply.github.com"
path: book/book/pandoc/paperback/100-exercises-to-learn-rust.pdf git add --force book/book
- uses: actions/upload-artifact@v4 git commit -m "Render book"
with: git push --set-upstream --force-with-lease origin deploy
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: formatter:
runs-on: ubuntu-latest runs-on: ubuntu-latest

463
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

Binary file not shown.

View File

@ -1,132 +1,12 @@
[book] [book]
authors = ["Luca Palmieri"] authors = ["Luca Palmieri (Mainmatter)"]
language = "en" language = "en"
multilingual = false multilingual = false
src = "src" src = "src"
title = "100 Exercises To Learn Rust" 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] [output.html]
git-repository-url = "https://github.com/mainmatter/100-exercises-to-learn-rust" git-repository-url = "https://github.com/mainmatter/100-exercises-to-learn-rust"
[output.html.playground] [preprocessor.exercise-linker]
runnable = false exercise_root_url = "https://github.com/mainmatter/100-exercises-to-learn-rust/tree/main/exercises"
[output.pandoc]
optional = true
hosted-html = "https://rust-exercises.com/100-exercises/"
[output.pandoc.code]
show-hidden-lines = true
[output.pandoc.profile.pdf] # options to pass to Pandoc (see https://pandoc.org/MANUAL.html)
output-file = "100-exercises-to-learn-rust.pdf"
to = "latex"
highlight-style = "./custom.theme"
# We use `lualatext` because, right now, it's the only engine
# that supports fallback fonts, which we need for emojis.
pdf-engine = "lualatex"
metadata-file = "metadata.yml"
[output.pandoc.profile.pdf.variables]
mainfont = "CoreSansA45.ttf"
mainfontoptions = [
"BoldFont=CoreSansA65.ttf",
"ItalicFont=CoreSansA45It.ttf",
"BoldItalicFont=CoreSansA65It.ttf",
]
sansfont = "CoreSansA45.ttf"
sansfontoptions = [
"BoldFont=CoreSansA65.ttf",
"ItalicFont=CoreSansA45It.ttf",
"BoldItalicFont=CoreSansA65It.ttf",
]
# You can get these fonts here: https://fonts.google.com/selection?query=noto+color+
monofont = "Noto Sans Mono"
mainfontfallback = ["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"

View File

@ -1,211 +0,0 @@
{
"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
}
}
}

View File

@ -1,196 +0,0 @@
{
"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"
}

View File

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

View File

@ -24,19 +24,14 @@ It has been designed to be interactive and hands-on.
to be delivered in a classroom setting, over 4 days: each attendee advances 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 through the lessons at their own pace, with an experienced instructor providing
guidance, answering questions and diving deeper into the topics as needed.\ guidance, answering questions and diving deeper into the topics as needed.\
You can sign up for the next tutored session on [our website](https://ti.to/mainmatter/rust-from-scratch-jan-2025). If you're interested in attending one of our training sessions, or if you'd like to
If you'd like to organise a private session for your company, please [get in touch](https://mainmatter.com/contact/). bring this course to your company, please [get in touch](https://mainmatter.com/contact/).
You can also take the courses on your own, but we recommend you find a friend or You can also follow the course 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 a mentor to help you along the way should you get stuck. You can
find solutions for all exercises in the also find solutions to all exercises in the
[`solutions` branch of the GitHub repository](https://github.com/mainmatter/100-exercises-to-learn-rust/tree/solutions). [`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 ## Structure
On the left side of the screen, you can see that the course is divided into sections. On the left side of the screen, you can see that the course is divided into sections.
@ -51,7 +46,8 @@ Before starting the course, make sure to clone the repository to your local mach
# If you have an SSH key set up with GitHub # If you have an SSH key set up with GitHub
git clone git@github.com:mainmatter/100-exercises-to-learn-rust.git git clone git@github.com:mainmatter/100-exercises-to-learn-rust.git
# Otherwise, use the HTTPS URL: # Otherwise, use the HTTPS URL:
# https://github.com/mainmatter/100-exercises-to-learn-rust.git #
# git clone 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 We also recommend you work on a branch, so you can easily track your progress and pull
@ -67,23 +63,17 @@ 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 The package contains the exercise itself, instructions on what to do (in `src/lib.rs`), and a test suite to
automatically verify your solution. automatically verify your solution.
### Tools ### `wr`, the workshop runner
To work through this course, you'll need: 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:
- [**Rust**](https://www.rust-lang.org/tools/install). ```bash
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. cargo install --locked workshop-runner
- _(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.
### Workshop runner, `wr` In a new terminal, navigate back to the top-level folder of the repository.
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: Run the `wr` command to start the course:
```bash ```bash

View File

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

View File

@ -36,38 +36,6 @@ 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 ## Booleans
The condition in an `if` expression must be of type `bool`, a **boolean**.\ The condition in an `if` expression must be of type `bool`, a **boolean**.\

View File

@ -69,20 +69,16 @@ 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 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. used to customize the way Rust code is compiled.
Cargo provides 4 built-in profiles: `dev`, `release`, `test`, and `bench`.\ Cargo provides two built-in profiles: `dev` and `release`.\
The `dev` profile is used every time you run `cargo build`, `cargo run` or `cargo test`. It's aimed at local The `dev` profile is used every time you run `cargo build`, `cargo run` or `cargo test`. It's aimed at local
development, development,
therefore it sacrifices runtime performance in favor of faster compilation times and a better debugging experience.\ 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 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`. 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.\ > "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 > It refers to developers who are not familiar with Rust and complain about its performance on
> social media (e.g. Reddit, Twitter) before realizing they haven't built their project in > social media (e.g. Reddit, Twitter, etc.) before realizing they haven't built their project in
> release mode. > release mode.
You can also define custom profiles or customize the built-in ones. You can also define custom profiles or customize the built-in ones.

View File

@ -72,8 +72,7 @@ error: literal out of range for `i8`
4 | let a = 255 as i8; 4 | let a = 255 as i8;
| ^^^ | ^^^
| |
= note: the literal `255` does not fit into the type `i8` = note: the literal `255` does not fit into the type `i8` whose range is `-128..=127`
whose range is `-128..=127`
= help: consider using the type `u8` instead = help: consider using the type `u8` instead
= note: `#[deny(overflowing_literals)]` on by default = 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>, ... } // Syntax: <StructName> { <field_name>: <value>, ... }
let ticket = Ticket { let ticket = Ticket {
title: "Build a ticket system".into(), title: "Build a ticket system".into(),
description: "A Kanban board".into(), description: "Create a system that can manage tickets across a Kanban board".into(),
status: "Open".into() status: "Open".into()
}; };
``` ```
@ -130,8 +130,7 @@ let default_config = Configuration::default();
You can use the function call syntax even for methods that take `self` as their first parameter: You can use the function call syntax even for methods that take `self` as their first parameter:
```rust ```rust
// Function call syntax: // Function call syntax: <StructName>::<method_name>(<instance>, <parameters>)
// <StructName>::<method_name>(<instance>, <parameters>)
let is_open = Ticket::is_open(ticket); let is_open = Ticket::is_open(ticket);
``` ```

View File

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

View File

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

View File

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

View File

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

View File

@ -18,25 +18,11 @@ 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]. When the function returns, the stack frame is popped off the stack[^stack-overflow].
```text ```text
+-----------------+ +-----------------+
| frame for func1 | func2 | frame for func2 | func2
+-----------------+ +-----------------+ is called +-----------------+ returns +-----------------+
| | 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**.\ 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. 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. [^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://web.archive.org/web/20240517051950/https://blog.acolyer.org/2019/05/28/cheri-abi/)). In certain environments, a pointer is **larger** than a memory address (e.g. [CHERI](https://blog.acolyer.org/2019/05/28/cheri-abi/)).
Rust makes the simplifying assumption that pointers are the same size as memory addresses, 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. which is true for most modern systems you're likely to encounter.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -56,7 +56,7 @@ that to `consumer` instead.\
## In memory ## In memory
Let's look at what happened in memory in the example above. 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 ```text
s s

View File

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

View File

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

View File

@ -68,11 +68,3 @@ match status {
``` ```
The `_` pattern matches anything that wasn't matched by the previous patterns. 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); println!("Assigned to: {}", assigned_to);
}, },
Status::ToDo | Status::Done => { Status::ToDo | Status::Done => {
println!("ToDo or Done"); println!("Done");
} }
} }
``` ```
@ -82,7 +82,7 @@ match status {
println!("Assigned to: {}", person); println!("Assigned to: {}", person);
}, },
Status::ToDo | Status::Done => { Status::ToDo | Status::Done => {
println!("ToDo or Done"); println!("Done");
} }
} }
``` ```

View File

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

View File

@ -4,11 +4,7 @@ Let's revisit the `Ticket::new` function from the previous exercise:
```rust ```rust
impl Ticket { impl Ticket {
pub fn new( pub fn new(title: String, description: String, status: Status) -> Ticket {
title: String,
description: String,
status: Status
) -> Ticket {
if title.is_empty() { if title.is_empty() {
panic!("Title cannot be empty"); panic!("Title cannot be empty");
} }
@ -74,9 +70,8 @@ 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`. If a function can fail (and you want the caller to have a shot at handling the error), it must return a `Result`.
```rust ```rust
// Just by looking at the signature, you know that this function // Just by looking at the signature, you know that this function can fail.
// can fail. You can also inspect `ParseIntError` to see what // You can also inspect `ParseIntError` to see what kind of failures to expect.
// kind of failures to expect.
fn parse_int(s: &str) -> Result<i32, ParseIntError> { 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. // This won't compile: we're not handling the error case.
// We must either use `match` or one of the combinators provided by // We must either use `match` or one of the combinators provided by `Result`
// `Result` to "unwrap" the success value or handle the error. // to "unwrap" the success value or handle the error.
let number = parse_int("42") + 2; let number = parse_int("42") + 2;
``` ```

View File

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

View File

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

View File

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

View File

@ -21,15 +21,6 @@ let numbers: [u32; 3] = [1, 2, 3];
This creates an array of 3 integers, initialized with the values `1`, `2`, and `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". 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 ### Accessing elements
You can access elements of an array using square brackets: You can access elements of an array using square brackets:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,7 +58,7 @@ drop(guard)
## Locking granularity ## Locking granularity
What should our `Mutex` wrap?\ What should our `Mutex` wrap?\
The simplest option would be to wrap the entire `TicketStore` in a single `Mutex`.\ The simplest option would be the 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, 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.\ because every read would have to wait for the lock to be released.\
This is known as **coarse-grained locking**. This is known as **coarse-grained locking**.
@ -99,7 +99,7 @@ fn main() {
let guard = lock.lock().unwrap(); let guard = lock.lock().unwrap();
spawn(move || { spawn(move || {
receiver.recv().unwrap(); receiver.recv().unwrap();;
}); });
// Try to send the guard over the channel // Try to send the guard over the channel
@ -111,23 +111,19 @@ fn main() {
The compiler is not happy with this code: The compiler is not happy with this code:
```text ```text
error[E0277]: `MutexGuard<'_, i32>` cannot be sent between error[E0277]: `MutexGuard<'_, i32>` cannot be sent between threads safely
threads safely
--> src/main.rs:10:7 --> src/main.rs:10:7
| |
10 | spawn(move || { 10 | spawn(move || {
| _-----_^ | _-----_^
| | | | | |
| | required by a bound introduced by this call | | required by a bound introduced by this call
11 | | receiver.recv().unwrap(); 11 | | receiver.recv().unwrap();;
12 | | }); 12 | | });
| |_^ `MutexGuard<'_, i32>` cannot be sent between threads safely | |_^ `MutexGuard<'_, i32>` cannot be sent between threads safely
| |
= help: the trait `Send` is not implemented for = help: the trait `Send` is not implemented for `MutexGuard<'_, i32>`, which is required by `{closure@src/main.rs:10:7: 10:14}: Send`
`MutexGuard<'_, i32>`, which is required by = note: required for `std::sync::mpsc::Receiver<MutexGuard<'_, i32>>` to implement `Send`
`{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 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`.\ `Sync` is an auto trait, just like `Send`.\
It is automatically implemented by all types that can be safely **shared** between threads. It is automatically implemented by all types that can be safely **shared** between threads.
In order words: `T` is Sync if `&T` is `Send`. In order words: `T: Sync` means that `&T` is `Send`.
## `T: Sync` doesn't imply `T: Send` ## `Sync` doesn't imply `Send`
It's important to note that `T` can be `Sync` without being `Send`.\ It's important to note that `Sync` doesn't imply `Send`.\
For example: `MutexGuard` is not `Send`, but it is `Sync`. 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 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.\ 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. But it is `Sync`, because giving a `&MutexGuard` to another thread has no impact on where the lock is released.
## `T: Send` doesn't imply `T: Sync` ## `Send` doesn't imply `Sync`
The opposite is also true: `T` can be `Send` without being `Sync`.\ The opposite is also true: `Send` doesn't imply `Sync`.\
For example: `RefCell<T>` is `Send` (if `T` is `Send`), but it is not `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. `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. 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. That's where you're supposed to set up and launch your chosen async runtime.
Most runtimes provide a macro to make this easier. For `tokio`, it's `tokio::main`: Most runtimes provides a macro to make this easier. For `tokio`, it's `tokio::main`:
```rust ```rust
#[tokio::main] #[tokio::main]

View File

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

View File

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

View File

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

View File

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

View File

@ -105,5 +105,5 @@ async fn run() {
Check out [`select!`'s documentation](https://tokio.rs/tokio/tutorial/select) for more details.\ 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 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. [`StreamExt::merge`](https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html#method.merge) instead.
- A [`CancellationToken`](https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html) may be - Rather than "abrupt" cancellation, it can be preferable to rely
preferable to `JoinHandle::abort` in some cases. on [`CancellationToken`](https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html).

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.\ You can then find resources on key topics that cut across domains.\
For testing, check out For testing, check out
["Advanced testing, going beyond the basics"](https://rust-exercises.com/advanced-testing/).\ ["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://rust-exercises.com/telemetry/). For telemetry, check out ["You can't fix what you can't see"](https://github.com/mainmatter/rust-telemetry-workshop).

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,9 +2,3 @@
name = "panics" name = "panics"
version = "0.1.0" version = "0.1.0"
edition = "2021" 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,7 +2,9 @@
/// calculate the average speed of the journey. /// calculate the average speed of the journey.
fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 { fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: Panic with a custom message if `time_elapsed` is 0 // TODO: Panic with a custom message if `time_elapsed` is 0
if time_elapsed == 0 {
panic!("The journey took no time at all, that's impossible!");
}
(end - start) / time_elapsed (end - start) / time_elapsed
} }
@ -19,7 +21,7 @@ mod tests {
// 👇 With the `#[should_panic]` annotation we can assert that we expect the code // 👇 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`. // 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! // 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() { fn by_zero() {
speed(0, 10, 0); speed(0, 10, 0);
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -2,9 +2,3 @@
name = "for_" name = "for_"
version = "0.1.0" version = "0.1.0"
edition = "2021" 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,10 @@
// Rewrite the factorial function using a `for` loop. // Rewrite the factorial function using a `for` loop.
pub fn factorial(n: u32) -> u32 { pub fn factorial(n: u32) -> u32 {
todo!() let mut result: u32 = 1; // base case
for i in 2..=n {
result *= i;
}
result
} }
#[cfg(test)] #[cfg(test)]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,9 +5,3 @@ edition = "2021"
[dev-dependencies] [dev-dependencies]
common = { path = "../../../helpers/common" } 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,13 +12,27 @@ impl Ticket {
// - the `title` should be at most 50 bytes long. // - the `title` should be at most 50 bytes long.
// - the `description` should be at most 500 bytes long. // - the `description` should be at most 500 bytes long.
// The method should panic if any of the requirements are not met. // 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, // 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 // as well as some `String` methods. Use the documentation of Rust's standard library
// to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html // to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html
fn new(title: String, description: String, status: String) -> Self { fn new(title: String, description: String, status: String) -> Self {
todo!(); if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed")
}
if title.is_empty() {
panic!("Title cannot be empty")
}
if description.is_empty() {
panic!("Description cannot be empty")
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes")
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes")
}
Self { Self {
title, title,
description, description,

View File

@ -2,9 +2,3 @@
name = "modules" name = "modules"
version = "0.1.0" version = "0.1.0"
edition = "2021" 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 @@
mod helpers { mod helpers {
// TODO: Make this code compile, either by adding a `use` statement or by using // TODO: Make this code compile, either by adding a `use` statement or by using
// the appropriate path to refer to the `Ticket` struct. // the appropriate path to refer to the `Ticket` struct.
use super::Ticket;
fn create_todo_ticket(title: String, description: String) -> Ticket { fn create_todo_ticket(title: String, description: String) -> Ticket {
Ticket::new(title, description, "To-Do".into()) Ticket::new(title, description, "To-Do".into())

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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