Showing preview only (260K chars total). Download the full file or copy to clipboard to get everything.
Repository: mainmatter/100-exercises-to-learn-rust
Branch: main
Commit: 8791feb495a9
Files: 247
Total size: 205.6 KB
Directory structure:
gitextract_716k_gfd/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .wr.toml
├── Cargo.toml
├── README.md
├── dprint.json
├── exercises/
│ ├── 01_intro/
│ │ ├── 00_welcome/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 01_syntax/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 02_basic_calculator/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_integers/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_variables/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_if_else/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_panics/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_factorial/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_while/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_for/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_overflow/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_saturating/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 10_as_casting/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 03_ticket_v1/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_struct/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_validation/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_modules/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_visibility/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_encapsulation/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_ownership/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_setters/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_stack/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_heap/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 10_references_in_memory/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 11_destructor/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 12_outro/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── tests/
│ │ └── integration.rs
│ ├── 04_traits/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_trait/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_orphan_rule/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_operator_overloading/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_derive/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_trait_bounds/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_str_slice/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_deref/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_sized/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_from/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 10_assoc_vs_generic/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 11_clone/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 12_copy/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 13_drop/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 14_outro/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── tests/
│ │ └── integration.rs
│ ├── 05_ticket_v2/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_enum/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_match/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_variants_with_data/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_if_let/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_nullability/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_fallibility/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_unwrap/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_error_enums/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_error_trait/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 10_packages/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── main.rs
│ │ ├── 11_dependencies/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 12_thiserror/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 13_try_from/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 14_source/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ ├── lib.rs
│ │ │ └── status.rs
│ │ └── 15_outro/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── description.rs
│ │ ├── lib.rs
│ │ ├── status.rs
│ │ └── title.rs
│ ├── 06_ticket_management/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_arrays/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_vec/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_resizing/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_iterators/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_iter/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_lifetimes/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_combinators/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_impl_trait/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_impl_trait_2/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 10_slices/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 11_mutable_slices/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 12_two_states/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 13_index/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 14_index_mut/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 15_hashmap/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 16_btreemap/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 07_threads/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_threads/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_static/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_leak/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_scoped_threads/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_channels/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── insert.rs
│ │ ├── 06_interior_mutability/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_ack/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── insert.rs
│ │ ├── 08_client/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── insert.rs
│ │ ├── 09_bounded/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── insert.rs
│ │ ├── 10_patch/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── check.rs
│ │ ├── 11_locks/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── check.rs
│ │ ├── 12_rw_lock/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── check.rs
│ │ ├── 13_without_channels/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── check.rs
│ │ └── 14_sync/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── 08_futures/
│ ├── 00_intro/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 01_async_fn/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 02_spawn/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 03_runtime/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 04_future/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 05_blocking/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 06_async_aware_primitives/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 07_cancellation/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── 08_outro/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── helpers/
│ ├── common/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── json2redirects.sh
│ ├── mdbook-exercise-linker/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── mdbook-link-shortener/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── main.rs
│ └── ticket_fields/
│ ├── Cargo.toml
│ └── src/
│ ├── description.rs
│ ├── lib.rs
│ ├── test_helpers.rs
│ └── title.rs
└── site/
└── _redirects
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/ci.yml
================================================
name: "CI"
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
schedule:
# First day of a month
- cron: '0 0 1 * *'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get Core Sans
uses: actions/checkout@v4
if: "!github.event.pull_request.head.repo.fork"
with:
fetch-depth: 0
repository: mainmatter/core-sans-a-fonts
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
path: core-sans-a-fonts
- name: Install Core Sans Font
if: "!github.event.pull_request.head.repo.fork"
run: |
sudo cp -r core-sans-a-fonts/* /usr/local/share/fonts/
sudo fc-cache -f -v
fc-list | grep "Core Sans"
- name: Use Fallback font for fork PRs
if: "github.event.pull_request.head.repo.fork"
run: |
sed -i 's/"BoldFont=CoreSansA65.ttf",//g' book/book.toml
sed -i 's/"ItalicFont=CoreSansA45It.ttf",//g' book/book.toml
sed -i 's/"BoldItalicFont=CoreSansA65It.ttf",//g' book/book.toml
sed -i 's/CoreSansA45.ttf/Open Sans:style=Regular/g' book/book.toml
- uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install exercise plugin
run: cargo install --path helpers/mdbook-exercise-linker
- name: Install link shortener plugin
run: cargo install --path helpers/mdbook-link-shortener
- name: Install mdbook-pandoc, calibre, pdftk and related dependencies
run: |
cargo install mdbook-pandoc --locked --version 0.7.1
sudo apt-get update
sudo apt-get install -y fonts-noto fonts-open-sans calibre pdftk
sudo fc-cache -f -v
export PANDOC_VERSION=3.3
curl -LsSf https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-linux-amd64.tar.gz | tar zxf -
echo "$PWD/pandoc-${PANDOC_VERSION}/bin" >> $GITHUB_PATH
shell: bash
- name: Setup TeX Live
uses: TeX-Live/setup-texlive-action@v3
with:
packages:
scheme-basic
luatex
lualatex-math
luacolor
luatexbase
luaotfload
framed
unicode-math
xcolor
geometry
longtable
booktabs
array
lua-ul
etoolbox
fancyvrb
footnote
selnolig
natbib
csquotes
bookmark
xurl
amsmath
setspace
iftex
- name: Check `tlmgr` version
run: tlmgr --version
- uses: taiki-e/install-action@v2
with:
tool: mdbook
- name: Build book
env:
LINK_SHORTENER_VERIFY: "true"
run: |
cd book
mdbook build
- name: Add cover and back to downloadable PDF
run: |
pdftk book/assets/cover.pdf book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf book/assets/back.pdf cat output book/book/pandoc/pdf/100-exercises-to-learn-rust-with-cover.pdf
mv book/book/pandoc/pdf/100-exercises-to-learn-rust-with-cover.pdf book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf
- name: Convert HTML to ePUB
run: |
cd book/book/pandoc/html
sed -i 's|<code>\\newpage</code>{=latex}||g' 100-exercises-to-learn-rust.html
ebook-convert 100-exercises-to-learn-rust.html 100-exercises-to-learn-rust.epub \
--embed-all-fonts \
--subset-embedded-fonts
- name: Link Checker
uses: lycheeverse/lychee-action@v1
with:
fail: true
args: |
--exclude-loopback
--require-https
--no-progress
book/book/html/
# Upload the HTML book as an artifact
- uses: actions/upload-artifact@v4
with:
name: book
# When you support multiple formats, the output directory changes
# to include the format in its path.
path: book/book/html
- uses: actions/upload-artifact@v4
with:
name: online-pdf
path: book/book/pandoc/pdf/100-exercises-to-learn-rust.pdf
- uses: actions/upload-artifact@v4
with:
name: paperback
path: book/book/pandoc/paperback/100-exercises-to-learn-rust.pdf
- uses: actions/upload-artifact@v4
with:
name: ePUB
path: book/book/pandoc/html/100-exercises-to-learn-rust.epub
is_fresh:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: sudo apt-get update && sudo apt-get install -y jq
- run: |
./helpers/json2redirects.sh book/link2alias.json > site/_redirects
# Verify nothing has changed, meaning that the redirect file is up-to-date
- run: |
git diff --exit-code site/_redirects
gravity:
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/download-artifact@v4
with:
path: book
pattern: online-pdf
- uses: pnpm/action-setup@v4
with:
version: 9
- run: ls -las ./book
- name: Run Gravity
run: pnpm dlx @gravityci/cli "./book/**/*"
env:
GRAVITY_TOKEN: ${{ secrets.GRAVITY_TOKEN }}
formatter:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dprint/check@v2.2
================================================
FILE: .gitignore
================================================
target/
exercises/progress.db
================================================
FILE: .wr.toml
================================================
================================================
FILE: Cargo.toml
================================================
[workspace]
members = [
"exercises/*/*",
"helpers/common",
"helpers/mdbook-exercise-linker",
"helpers/mdbook-link-shortener",
"helpers/ticket_fields",
]
resolver = "2"
# This is needed to guarantee the expected behaviour on that specific exercise,
# regardless of the "global" setting for `overflow-checks` on the `dev` profile.
[profile.dev.package.copy]
overflow-checks = true
================================================
FILE: README.md
================================================
# Learn Rust, one exercise at a time
You've heard about Rust, but you never had the chance to try it out?\
This course is for you!
You'll learn Rust by solving 100 exercises.\
You'll go from knowing nothing about Rust to being able to start
writing your own programs, one exercise at a time.
> [!NOTE]
> This course has been written by [Mainmatter](https://mainmatter.com/rust-consulting/).\
> It's one of the trainings in [our portfolio of Rust workshops](https://mainmatter.com/services/workshops/rust/).\
> Check out our [landing page](https://mainmatter.com/rust-consulting/) if you're looking for Rust consulting or
> training!
## Getting started
Go to [rust-exercises.com](https://rust-exercises.com) and follow the instructions there
to get started with the course.
## Requirements
- **Rust** (follow instructions [here](https://www.rust-lang.org/tools/install)).\
If `rustup` is already installed on your system, run `rustup update` (or another appropriate command depending on how
you installed Rust on your system)
to make sure you're running on the latest stable version.
- _(Optional but recommended)_ An IDE with Rust autocompletion support.
We recommend one of the following:
- [RustRover](https://www.jetbrains.com/rust/);
- [Visual Studio Code](https://code.visualstudio.com) with
the [`rust-analyzer`](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer) extension.
## Solutions
You can find the solutions to the exercises in
the [`solutions` branch](https://github.com/mainmatter/100-exercises-to-learn-rust/tree/solutions) of this repository.
# License
Copyright © 2024- Mainmatter GmbH (https://mainmatter.com), released under the
[Creative Commons Attribution-NonCommercial 4.0 International license](https://creativecommons.org/licenses/by-nc/4.0/).
================================================
FILE: dprint.json
================================================
{
"markdown": {
},
"toml": {
},
"excludes": [],
"plugins": [
"https://plugins.dprint.dev/markdown-0.17.0.wasm",
"https://plugins.dprint.dev/toml-0.6.1.wasm"
]
}
================================================
FILE: exercises/01_intro/00_welcome/Cargo.toml
================================================
[package]
name = "welcome_00"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/01_intro/00_welcome/src/lib.rs
================================================
// This is a Rust file. It is a plain text file with a `.rs` extension.
//
// Like most modern programming languages, Rust supports comments. You're looking at one right now!
// Comments are ignored by the compiler; you can leverage them to annotate code with notes and
// explanations.
// There are various ways to write comments in Rust, each with its own purpose.
// For now we'll stick to the most common one: the line comment.
// Everything from `//` to the end of the line is considered a comment.
// Exercises will include `TODO`, `todo!()` or `__` markers to draw your attention to the lines
// where you need to write code.
// You'll need to replace these markers with your own code to complete the exercise.
// Sometimes it'll be enough to write a single line of code, other times you'll have to write
// longer sections.
//
// If you get stuck for more than 10 minutes on an exercise, grab a trainer! We're here to help!
// You can also find solutions to all exercises in the `solutions` git branch.
fn greeting() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
// Your solutions will be automatically verified by a set of tests.
// You can run these tests directly by invoking the `cargo test` command in your terminal,
// from the root of this exercise's directory. That's what the `wr` command does for you
// under the hood.
//
// Rust lets you write tests alongside your code.
// The `#[cfg(test)]` attribute tells the compiler to only compile the code below when
// running tests (i.e. when you run `cargo test`).
// You'll learn more about attributes and testing later in the course.
// For now, just know that you need to look for the `#[cfg(test)]` attribute to find the tests
// that will be verifying the correctness of your solutions!
//
// ⚠️ **DO NOT MODIFY THE TESTS** ⚠️
// They are there to help you validate your solutions. You should only change the code that's being
// tested, not the tests themselves.
#[cfg(test)]
mod tests {
use crate::greeting;
#[test]
fn test_welcome() {
assert_eq!(greeting(), "I'm ready to learn Rust!");
}
}
================================================
FILE: exercises/01_intro/01_syntax/Cargo.toml
================================================
[package]
name = "syntax"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/01_intro/01_syntax/src/lib.rs
================================================
// TODO: fix the function signature below to make the tests pass.
// Make sure to read the compiler error message—the Rust compiler is your pair programming
// partner in this course and it'll often guide you in the right direction!
//
// The input parameters should have the same type of the return type.
fn compute(a, b) -> u32 {
// Don't touch the function body.
a + b * 2
}
#[cfg(test)]
mod tests {
use crate::compute;
#[test]
fn case() {
assert_eq!(compute(1, 2), 5);
}
}
================================================
FILE: exercises/02_basic_calculator/00_intro/Cargo.toml
================================================
[package]
name = "intro_01"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/00_intro/src/lib.rs
================================================
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to build a calculator in Rust!");
}
}
================================================
FILE: exercises/02_basic_calculator/01_integers/Cargo.toml
================================================
[package]
name = "integers"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/01_integers/src/lib.rs
================================================
fn compute(a: u32, b: u32) -> u32 {
// TODO: change the line below to fix the compiler error and make the tests pass.
let multiplier: u8 = 4;
a + b * multiplier
}
#[cfg(test)]
mod tests {
use crate::compute;
#[test]
fn case() {
assert_eq!(compute(1, 2), 9);
}
}
================================================
FILE: exercises/02_basic_calculator/02_variables/Cargo.toml
================================================
[package]
name = "variables"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/02_variables/src/lib.rs
================================================
// 👇 The lines below, starting with `///`, are called **documentation comments**.
// They attach documentation to the item that follows them. In this case, the `speed` function.
// If you run `cargo doc --open` from this exercise's directory, Rust will generate
// HTML documentation from these comments and open it in your browser.
/// Given the start and end points of a journey, and the time it took to complete it,
/// calculate the average speed.
pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: define a variable named `distance` with the right value to get tests to pass
// Do you need to annotate the type of `distance`? Why or why not?
// Don't change the line below
distance / time_elapsed
}
#[cfg(test)]
mod tests {
use crate::speed;
#[test]
fn case1() {
assert_eq!(speed(0, 10, 10), 1);
}
#[test]
fn case2() {
assert_eq!(speed(10, 30, 10), 2);
}
#[test]
fn case3() {
assert_eq!(speed(10, 31, 10), 2);
}
}
================================================
FILE: exercises/02_basic_calculator/03_if_else/Cargo.toml
================================================
[package]
name = "if_else"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/03_if_else/src/lib.rs
================================================
/// Return `12` if `n` is even,
/// `13` if `n` is divisible by `3`,
/// `17` otherwise.
fn magic_number(n: u32) -> u32 {
todo!()
}
#[cfg(test)]
mod tests {
use crate::magic_number;
#[test]
fn one() {
assert_eq!(magic_number(1), 17);
}
#[test]
fn two() {
assert_eq!(magic_number(2), 12);
}
#[test]
fn six() {
assert_eq!(magic_number(6), 12);
}
#[test]
fn nine() {
assert_eq!(magic_number(9), 13);
}
#[test]
fn high() {
assert_eq!(magic_number(233), 17);
}
}
================================================
FILE: exercises/02_basic_calculator/04_panics/Cargo.toml
================================================
[package]
name = "panics"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/04_panics/src/lib.rs
================================================
/// Given the start and end points of a journey, and the time it took to complete the journey,
/// calculate the average speed of the journey.
fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: Panic with a custom message if `time_elapsed` is 0
(end - start) / time_elapsed
}
#[cfg(test)]
mod tests {
use crate::speed;
#[test]
fn case1() {
assert_eq!(speed(0, 10, 10), 1);
}
#[test]
// 👇 With the `#[should_panic]` annotation we can assert that we expect the code
// under test to panic. We can also check the panic message by using `expected`.
// This is all part of Rust's built-in test framework!
#[should_panic(expected = "The journey took no time at all. That's impossible!")]
fn by_zero() {
speed(0, 10, 0);
}
}
================================================
FILE: exercises/02_basic_calculator/05_factorial/Cargo.toml
================================================
[package]
name = "factorial"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/05_factorial/src/lib.rs
================================================
// Define a function named `factorial` that, given a non-negative integer `n`,
// returns `n!`, the factorial of `n`.
//
// The factorial of `n` is defined as the product of all positive integers up to `n`.
// For example, `5!` (read "five factorial") is `5 * 4 * 3 * 2 * 1`, which is `120`.
// `0!` is defined to be `1`.
//
// We expect `factorial(0)` to return `1`, `factorial(1)` to return `1`,
// `factorial(2)` to return `2`, and so on.
//
// Use only what you learned! No loops yet, so you'll have to use recursion!
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}
================================================
FILE: exercises/02_basic_calculator/06_while/Cargo.toml
================================================
[package]
name = "while_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/06_while/src/lib.rs
================================================
// Rewrite the factorial function using a `while` loop.
pub fn factorial(n: u32) -> u32 {
// The `todo!()` macro is a placeholder that the compiler
// interprets as "I'll get back to this later", thus
// suppressing type errors.
// It panics at runtime.
todo!()
}
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}
================================================
FILE: exercises/02_basic_calculator/07_for/Cargo.toml
================================================
[package]
name = "for_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/07_for/src/lib.rs
================================================
// Rewrite the factorial function using a `for` loop.
pub fn factorial(n: u32) -> u32 {
todo!()
}
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}
================================================
FILE: exercises/02_basic_calculator/08_overflow/Cargo.toml
================================================
[package]
name = "overflow"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/08_overflow/src/lib.rs
================================================
// Customize the `dev` profile to wrap around on overflow.
// Check Cargo's documentation to find out the right syntax:
// https://doc.rust-lang.org/cargo/reference/profiles.html
//
// For reasons that we'll explain later, the customization needs to be done in the `Cargo.toml`
// at the root of the repository, not in the `Cargo.toml` of the exercise.
pub fn factorial(n: u32) -> u32 {
let mut result = 1;
for i in 1..=n {
result *= i;
}
result
}
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn twentieth() {
// 20! is 2432902008176640000, which is too large to fit in a u32
// With the default dev profile, this will panic when you run `cargo test`
// We want it to wrap around instead
assert_eq!(factorial(20), 2_192_834_560);
// ☝️
// A large number literal using underscores to improve readability!
}
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}
================================================
FILE: exercises/02_basic_calculator/09_saturating/Cargo.toml
================================================
[package]
name = "saturating"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/09_saturating/src/lib.rs
================================================
pub fn factorial(n: u32) -> u32 {
let mut result = 1;
for i in 1..=n {
// Use saturating multiplication to stop at the maximum value of u32
// rather than overflowing and wrapping around
result *= i;
}
result
}
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn twentieth() {
assert_eq!(factorial(20), u32::MAX);
}
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}
================================================
FILE: exercises/02_basic_calculator/10_as_casting/Cargo.toml
================================================
[package]
name = "as_cast"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/02_basic_calculator/10_as_casting/src/lib.rs
================================================
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct value after the conversion.
#[cfg(test)]
mod tests {
#[test]
fn u16_to_u32() {
let v: u32 = todo!();
assert_eq!(47u16 as u32, v);
}
#[test]
fn u8_to_i8() {
// The compiler is smart enough to know that the value 255 cannot fit
// inside an i8, so it'll emit a hard error. We intentionally disable
// this guardrail to make this (bad) conversion possible.
// The compiler is only able to pick on this because the value is a
// literal. If we were to use a variable, the compiler wouldn't be able to
// catch this at compile time.
#[allow(overflowing_literals)]
let x = { 255 as i8 };
// You could solve this by using exactly the same expression as above,
// but that would defeat the purpose of the exercise. Instead, use a genuine
// `i8` value that is equivalent to `255` when converted to `u8`.
let y: i8 = todo!();
assert_eq!(x, y);
}
#[test]
fn bool_to_u8() {
let v: u8 = todo!();
assert_eq!(true as u8, v);
}
}
================================================
FILE: exercises/03_ticket_v1/00_intro/Cargo.toml
================================================
[package]
name = "intro_02"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/03_ticket_v1/00_intro/src/lib.rs
================================================
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to start modelling a software ticket!");
}
}
================================================
FILE: exercises/03_ticket_v1/01_struct/Cargo.toml
================================================
[package]
name = "struct_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/03_ticket_v1/01_struct/src/lib.rs
================================================
// Define a struct named `Order` with the following fields:
// - `price`, an unsigned integer
// - `quantity`, an unsigned integer
//
// It should also have a method named `is_available` that returns a `true` if the quantity is
// greater than 0, otherwise `false`.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_order_is_available() {
let order = Order {
price: 100,
quantity: 10,
};
assert!(order.is_available());
}
#[test]
fn test_order_is_not_available() {
let order = Order {
price: 100,
quantity: 0,
};
assert!(!order.is_available());
}
}
================================================
FILE: exercises/03_ticket_v1/02_validation/Cargo.toml
================================================
[package]
name = "validation"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/03_ticket_v1/02_validation/src/lib.rs
================================================
struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
// TODO: implement the `new` function.
// The following requirements should be met:
// - Only `To-Do`, `In Progress`, and `Done` statuses are allowed.
// - The `title` and `description` fields should not be empty.
// - the `title` should be at most 50 bytes long.
// - the `description` should be at most 500 bytes long.
// The method should panic if any of the requirements are not met.
// You can find the needed panic messages in the tests.
//
// You'll have to use what you learned in the previous exercises,
// as well as some `String` methods. Use the documentation of Rust's standard library
// to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html
fn new(title: String, description: String, status: String) -> Self {
todo!();
Self {
title,
description,
status,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
#[should_panic(expected = "Title cannot be empty")]
fn title_cannot_be_empty() {
Ticket::new("".into(), valid_description(), "To-Do".into());
}
#[test]
#[should_panic(expected = "Description cannot be empty")]
fn description_cannot_be_empty() {
Ticket::new(valid_title(), "".into(), "To-Do".into());
}
#[test]
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
fn title_cannot_be_longer_than_fifty_chars() {
Ticket::new(overly_long_title(), valid_description(), "To-Do".into());
}
#[test]
#[should_panic(expected = "Description cannot be longer than 500 bytes")]
fn description_cannot_be_longer_than_500_chars() {
Ticket::new(valid_title(), overly_long_description(), "To-Do".into());
}
#[test]
#[should_panic(expected = "Only `To-Do`, `In Progress`, and `Done` statuses are allowed")]
fn status_must_be_valid() {
Ticket::new(valid_title(), valid_description(), "Funny".into());
}
#[test]
fn done_is_allowed() {
Ticket::new(valid_title(), valid_description(), "Done".into());
}
#[test]
fn in_progress_is_allowed() {
Ticket::new(valid_title(), valid_description(), "In Progress".into());
}
}
================================================
FILE: exercises/03_ticket_v1/03_modules/Cargo.toml
================================================
[package]
name = "modules"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"
================================================
FILE: exercises/03_ticket_v1/03_modules/src/lib.rs
================================================
mod helpers {
// TODO: Make this code compile, either by adding a `use` statement or by using
// the appropriate path to refer to the `Ticket` struct.
fn create_todo_ticket(title: String, description: String) -> Ticket {
Ticket::new(title, description, "To-Do".into())
}
}
struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket {
title,
description,
status,
}
}
}
================================================
FILE: exercises/03_ticket_v1/04_visibility/Cargo.toml
================================================
[package]
name = "visibility"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/03_ticket_v1/04_visibility/src/lib.rs
================================================
mod ticket {
struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket {
title,
description,
status,
}
}
}
}
// TODO: **Exceptionally**, you'll be modifying both the `ticket` module and the `tests` module
// in this exercise.
#[cfg(test)]
mod tests {
// TODO: Add the necessary `pub` modifiers in the parent module to remove the compiler
// errors about the use statement below.
use super::ticket::Ticket;
// Be careful though! We don't want this function to compile after you have changed
// visibility to make the use statement compile!
// Once you have verified that it indeed doesn't compile, comment it out.
fn should_not_be_possible() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
// You should be seeing this error when trying to run this exercise:
//
// error[E0616]: field `description` of struct `Ticket` is private
// |
// | assert_eq!(ticket.description, "A description");
// | ^^^^^^^^^^^^^^^^^^
//
// TODO: Once you have verified that the below does not compile,
// comment the line out to move on to the next exercise!
assert_eq!(ticket.description, "A description");
}
fn encapsulation_cannot_be_violated() {
// This should be impossible as well, with a similar error as the one encountered above.
// (It will throw a compilation error only after you have commented the faulty line
// in the previous test - next compilation stage!)
//
// This proves that `Ticket::new` is now the only way to get a `Ticket` instance.
// It's impossible to create a ticket with an illegal title or description!
//
// TODO: Once you have verified that the below does not compile,
// comment the lines out to move on to the next exercise!
let ticket = Ticket {
title: "A title".into(),
description: "A description".into(),
status: "To-Do".into(),
};
}
}
================================================
FILE: exercises/03_ticket_v1/05_encapsulation/Cargo.toml
================================================
[package]
name = "encapsulation"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/03_ticket_v1/05_encapsulation/src/lib.rs
================================================
pub mod ticket {
pub struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket {
title,
description,
status,
}
}
// TODO: Add three public methods to the `Ticket` struct:
// - `title` that returns the `title` field.
// - `description` that returns the `description` field.
// - `status` that returns the `status` field.
}
}
#[cfg(test)]
mod tests {
use super::ticket::Ticket;
#[test]
fn description() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
assert_eq!(ticket.description(), "A description");
}
#[test]
fn title() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
assert_eq!(ticket.title(), "A title");
}
#[test]
fn status() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
assert_eq!(ticket.status(), "To-Do");
}
}
================================================
FILE: exercises/03_ticket_v1/06_ownership/Cargo.toml
================================================
[package]
name = "ownership"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/03_ticket_v1/06_ownership/src/lib.rs
================================================
// TODO: based on what we just learned about ownership, it sounds like immutable references
// are a good fit for our accessor methods.
// Change the existing implementation of `Ticket`'s accessor methods to take a reference
// to `self` as an argument, rather than taking ownership of it.
pub struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket {
title,
description,
status,
}
}
pub fn title(self) -> String {
self.title
}
pub fn description(self) -> String {
self.description
}
pub fn status(self) -> String {
self.status
}
}
#[cfg(test)]
mod tests {
use super::Ticket;
#[test]
fn works() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
// If you change the signatures as requested, this should compile:
// we can call these methods one after the other because they borrow `self`
// rather than taking ownership of it.
assert_eq!(ticket.title(), "A title");
assert_eq!(ticket.description(), "A description");
assert_eq!(ticket.status(), "To-Do");
}
}
================================================
FILE: exercises/03_ticket_v1/07_setters/Cargo.toml
================================================
[package]
name = "setters"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/03_ticket_v1/07_setters/src/lib.rs
================================================
// TODO: Add &mut-setters to the `Ticket` struct for each of its fields.
// Make sure to enforce the same validation rules you have in `Ticket::new`!
// Even better, extract that logic and reuse it in both places. You can use
// private functions or private static methods for that.
pub struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket {
title,
description,
status,
}
}
pub fn title(&self) -> &String {
&self.title
}
pub fn description(&self) -> &String {
&self.description
}
pub fn status(&self) -> &String {
&self.status
}
}
#[cfg(test)]
mod tests {
use super::Ticket;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
fn works() {
let mut ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
ticket.set_title("A new title".into());
ticket.set_description("A new description".into());
ticket.set_status("Done".into());
assert_eq!(ticket.title(), "A new title");
assert_eq!(ticket.description(), "A new description");
assert_eq!(ticket.status(), "Done");
}
#[test]
#[should_panic(expected = "Title cannot be empty")]
fn title_cannot_be_empty() {
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_title("".into());
}
#[test]
#[should_panic(expected = "Description cannot be empty")]
fn description_cannot_be_empty() {
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_description("".into());
}
#[test]
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
fn title_cannot_be_longer_than_fifty_chars() {
Ticket::new(valid_title(), valid_description(), "To-Do".into())
.set_title(overly_long_title())
}
#[test]
#[should_panic(expected = "Description cannot be longer than 500 bytes")]
fn description_cannot_be_longer_than_500_chars() {
Ticket::new(valid_title(), valid_description(), "To-Do".into())
.set_description(overly_long_description())
}
#[test]
#[should_panic(expected = "Only `To-Do`, `In Progress`, and `Done` statuses are allowed")]
fn status_must_be_valid() {
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_status("Funny".into());
}
}
================================================
FILE: exercises/03_ticket_v1/08_stack/Cargo.toml
================================================
[package]
name = "stack"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/03_ticket_v1/08_stack/src/lib.rs
================================================
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use std::mem::size_of;
#[test]
fn u16_size() {
assert_eq!(size_of::<u16>(), todo!());
}
#[test]
fn i32_size() {
assert_eq!(size_of::<i32>(), todo!());
}
#[test]
fn bool_size() {
assert_eq!(size_of::<bool>(), todo!());
}
}
================================================
FILE: exercises/03_ticket_v1/09_heap/Cargo.toml
================================================
[package]
name = "heap"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/03_ticket_v1/09_heap/src/lib.rs
================================================
pub struct Ticket {
title: String,
description: String,
status: String,
}
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use super::Ticket;
use std::mem::size_of;
#[test]
fn string_size() {
assert_eq!(size_of::<String>(), todo!());
}
#[test]
fn ticket_size() {
// This is a tricky question!
// The "intuitive" answer happens to be the correct answer this time,
// but, in general, the memory layout of structs is a more complex topic.
// If you're curious, check out the "Type layout" section of The Rust Reference
// https://doc.rust-lang.org/reference/type-layout.html for more information.
assert_eq!(size_of::<Ticket>(), todo!());
}
}
================================================
FILE: exercises/03_ticket_v1/10_references_in_memory/Cargo.toml
================================================
[package]
name = "references_in_memory"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/03_ticket_v1/10_references_in_memory/src/lib.rs
================================================
pub struct Ticket {
title: String,
description: String,
status: String,
}
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use super::Ticket;
use std::mem::size_of;
#[test]
fn u16_ref_size() {
assert_eq!(size_of::<&u16>(), todo!());
}
#[test]
fn u64_mut_ref_size() {
assert_eq!(size_of::<&mut u64>(), todo!());
}
#[test]
fn ticket_ref_size() {
assert_eq!(size_of::<&Ticket>(), todo!());
}
}
================================================
FILE: exercises/03_ticket_v1/11_destructor/Cargo.toml
================================================
[package]
name = "destructor"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/03_ticket_v1/11_destructor/src/lib.rs
================================================
// We need some more machinery to write a proper exercise for destructors.
// We'll pick the concept up again in a later chapter after covering traits and
// interior mutability.
fn outro() -> &'static str {
"I have a basic understanding of __!"
}
#[cfg(test)]
mod tests {
use crate::outro;
#[test]
fn test_outro() {
assert_eq!(outro(), "I have a basic understanding of destructors!");
}
}
================================================
FILE: exercises/03_ticket_v1/12_outro/Cargo.toml
================================================
[package]
name = "outro_02"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/03_ticket_v1/12_outro/src/lib.rs
================================================
// TODO: Define a new `Order` type.
// It should keep track of three pieces of information: `product_name`, `quantity`, and `unit_price`.
// The product name can't be empty and it can't be longer than 300 bytes.
// The quantity must be strictly greater than zero.
// The unit price is in cents and must be strictly greater than zero.
// Order must include a method named `total` that returns the total price of the order.
// Order must provide setters and getters for each field.
//
// Tests are located in a different place this time—in the `tests` folder.
// The `tests` folder is a special location for `cargo`. It's where it looks for **integration tests**.
// Integration here has a very specific meaning: they test **the public API** of your project.
// You'll need to pay attention to the visibility of your types and methods; integration
// tests can't access private or `pub(crate)` items.
================================================
FILE: exercises/03_ticket_v1/12_outro/tests/integration.rs
================================================
use outro_02::Order;
// Files inside the `tests` directory are only compiled when you run tests.
// As a consequence, we don't need the `#[cfg(test)]` attribute for conditional compilation—it's
// implied.
#[test]
fn test_order() {
let mut order = Order::new("Rusty Book".to_string(), 3, 2999);
assert_eq!(order.product_name(), "Rusty Book");
assert_eq!(order.quantity(), &3);
assert_eq!(order.unit_price(), &2999);
assert_eq!(order.total(), 8997);
order.set_product_name("Rust Book".to_string());
order.set_quantity(2);
order.set_unit_price(3999);
assert_eq!(order.product_name(), "Rust Book");
assert_eq!(order.quantity(), &2);
assert_eq!(order.unit_price(), &3999);
assert_eq!(order.total(), 7998);
}
// Validation tests
#[test]
#[should_panic]
fn test_empty_product_name() {
Order::new("".to_string(), 3, 2999);
}
#[test]
#[should_panic]
fn test_long_product_name() {
Order::new("a".repeat(301), 3, 2999);
}
#[test]
#[should_panic]
fn test_zero_quantity() {
Order::new("Rust Book".to_string(), 0, 2999);
}
#[test]
#[should_panic]
fn test_zero_unit_price() {
Order::new("Rust Book".to_string(), 3, 0);
}
================================================
FILE: exercises/04_traits/00_intro/Cargo.toml
================================================
[package]
name = "intro_03"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/00_intro/src/lib.rs
================================================
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to learn about traits!");
}
}
================================================
FILE: exercises/04_traits/01_trait/Cargo.toml
================================================
[package]
name = "trait_"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/01_trait/src/lib.rs
================================================
// Define a trait named `IsEven` that has a method `is_even` that returns a `true` if `self` is
// even, otherwise `false`.
//
// Then implement the trait for `u32` and `i32`.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_u32_is_even() {
assert!(42u32.is_even());
assert!(!43u32.is_even());
}
#[test]
fn test_i32_is_even() {
assert!(42i32.is_even());
assert!(!43i32.is_even());
assert!(0i32.is_even());
assert!(!(-1i32).is_even());
}
}
================================================
FILE: exercises/04_traits/02_orphan_rule/Cargo.toml
================================================
[package]
name = "orphan"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/02_orphan_rule/src/lib.rs
================================================
// TODO: this is an example of an orphan rule violation.
// We're implementing a foreign trait (`PartialEq`, from `std`) on
// a foreign type (`u32`, from `std`).
// Look at the compiler error to get familiar with what it looks like.
// Then delete the code below and move on to the next exercise.
impl PartialEq for u32 {
fn eq(&self, _other: &Self) -> bool {
todo!()
}
}
================================================
FILE: exercises/04_traits/03_operator_overloading/Cargo.toml
================================================
[package]
name = "overloading"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/03_operator_overloading/src/lib.rs
================================================
use std::cmp::PartialEq;
struct Ticket {
title: String,
description: String,
status: String,
}
// TODO: Implement the `PartialEq` trait for `Ticket`.
impl PartialEq for Ticket {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partial_eq() {
let title = "title";
let description = "description";
let status = "To-Do";
let ticket1 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: status.to_string(),
};
assert!(ticket1 == ticket2);
}
#[test]
fn test_description_not_matching() {
let title = "title";
let status = "To-Do";
let ticket1 = Ticket {
title: title.to_string(),
description: "description".to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: "description2".to_string(),
status: status.to_string(),
};
assert!(ticket1 != ticket2);
}
#[test]
fn test_title_not_matching() {
let status = "To-Do";
let description = "description";
let ticket1 = Ticket {
title: "title".to_string(),
description: description.to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: "title2".to_string(),
description: description.to_string(),
status: status.to_string(),
};
assert!(ticket1 != ticket2);
}
#[test]
fn test_status_not_matching() {
let title = "title";
let description = "description";
let ticket1 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: "status".to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: "status2".to_string(),
};
assert!(ticket1 != ticket2);
}
}
================================================
FILE: exercises/04_traits/04_derive/Cargo.toml
================================================
[package]
name = "derives"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/04_derive/src/lib.rs
================================================
// TODO: A (derivable) trait implementation is missing for this exercise to compile successfully.
// Fix it!
//
// # `Debug` primer
//
// `Debug` returns a representation of a Rust type that's suitable for debugging (hence the name).
// `assert_eq!` requires `Ticket` to implement `Debug` because, when the assertion fails, it tries to
// print both sides of the comparison to the terminal.
// If the compared type doesn't implement `Debug`, it doesn't know how to represent them!
#[derive(PartialEq)]
struct Ticket {
title: String,
description: String,
status: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partial_eq() {
let title = "title";
let description = "description";
let status = "To-Do";
let ticket1 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: status.to_string(),
};
assert_eq!(ticket1, ticket2);
}
#[test]
fn test_description_not_matching() {
let title = "title";
let status = "To-Do";
let ticket1 = Ticket {
title: title.to_string(),
description: "description".to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: "description2".to_string(),
status: status.to_string(),
};
assert_ne!(ticket1, ticket2);
}
#[test]
fn test_title_not_matching() {
let status = "To-Do";
let description = "description";
let ticket1 = Ticket {
title: "title".to_string(),
description: description.to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: "title2".to_string(),
description: description.to_string(),
status: status.to_string(),
};
assert_ne!(ticket1, ticket2);
}
#[test]
fn test_status_not_matching() {
let title = "title";
let description = "description";
let ticket1 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: "status".to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: "status2".to_string(),
};
assert_ne!(ticket1, ticket2);
}
}
================================================
FILE: exercises/04_traits/05_trait_bounds/Cargo.toml
================================================
[package]
name = "trait_bounds"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/05_trait_bounds/src/lib.rs
================================================
// TODO: Add the necessary trait bounds to `min` so that it compiles successfully.
// Refer to the documentation of the `std::cmp` module for more information on the traits you might need.
//
// Note: there are different trait bounds that'll make the compiler happy, but they come with
// different _semantics_. We'll cover those differences later in the course when we talk about ordered
// collections (e.g. BTreeMap).
/// Return the minimum of two values.
pub fn min<T>(left: T, right: T) -> T {
if left <= right {
left
} else {
right
}
}
================================================
FILE: exercises/04_traits/06_str_slice/Cargo.toml
================================================
[package]
name = "str_slice"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/04_traits/06_str_slice/src/lib.rs
================================================
// TODO: Re-implement `Ticket`'s accessor methods. This time return a `&str` rather than a `&String`.
pub struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket {
title,
description,
status,
}
}
pub fn title(&self) -> &String {
&self.title
}
pub fn description(&self) -> &String {
&self.description
}
pub fn status(&self) -> &String {
&self.status
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{valid_description, valid_title};
use std::any::{Any, TypeId};
#[test]
fn test_type() {
let ticket = Ticket::new(valid_title(), valid_description(), "To-Do".to_string());
// Some dark magic to verify that you used the expected return types
assert_eq!(TypeId::of::<str>(), ticket.title().type_id());
assert_eq!(TypeId::of::<str>(), ticket.description().type_id());
assert_eq!(TypeId::of::<str>(), ticket.status().type_id());
}
}
================================================
FILE: exercises/04_traits/07_deref/Cargo.toml
================================================
[package]
name = "deref"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/07_deref/src/lib.rs
================================================
// TODO: whenever `title` and `description` are returned via their accessor methods, they
// should be normalized—i.e. leading and trailing whitespace should be removed.
// There is a method in Rust's standard library that can help with this, but you won't
// find it in the documentation for `String`.
// Can you figure out where it is defined and how to use it?
pub struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
pub fn title(&self) -> &str {
todo!()
}
pub fn description(&self) -> &str {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normalization() {
let ticket = Ticket {
title: " A title ".to_string(),
description: " A description ".to_string(),
status: "To-Do".to_string(),
};
assert_eq!("A title", ticket.title());
assert_eq!("A description", ticket.description());
}
}
================================================
FILE: exercises/04_traits/08_sized/Cargo.toml
================================================
[package]
name = "sized"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/08_sized/src/lib.rs
================================================
pub fn example() {
// Trying to get the size of a str (or any other DST)
// via `std::mem::size_of` will result in a compile-time error.
//
// TODO: Comment out the following line and move on to the next exercise.
std::mem::size_of::<str>();
}
================================================
FILE: exercises/04_traits/09_from/Cargo.toml
================================================
[package]
name = "from"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/09_from/src/lib.rs
================================================
// TODO: Implement the `From` trait for the `WrappingU32` type to make `example` compile.
pub struct WrappingU32 {
value: u32,
}
fn example() {
let wrapping: WrappingU32 = 42.into();
let wrapping = WrappingU32::from(42);
}
================================================
FILE: exercises/04_traits/10_assoc_vs_generic/Cargo.toml
================================================
[package]
name = "assoc_vs_generic"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/10_assoc_vs_generic/src/lib.rs
================================================
// TODO: Define a new trait, `Power`, that has a method `power` that raises `self`
// to the power of `n`.
// The trait definition and its implementations should be enough to get
// the tests to compile and pass.
//
// Recommendation: you may be tempted to write a generic implementation to handle
// all cases at once. However, this is fairly complicated and requires the use of
// additional crates (i.e. `num-traits`).
// Even then, it might be preferable to use a simple macro instead to avoid
// the complexity of a highly generic implementation. Check out the
// "Little book of Rust macros" (https://veykril.github.io/tlborm/) if you're
// interested in learning more about it.
// You don't have to though: it's perfectly okay to write three separate
// implementations manually. Venture further only if you're curious.
#[cfg(test)]
mod tests {
use super::Power;
#[test]
fn test_power_u16() {
let x: u32 = 2_u32.power(3u16);
assert_eq!(x, 8);
}
#[test]
fn test_power_u32() {
let x: u32 = 2_u32.power(3u32);
assert_eq!(x, 8);
}
#[test]
fn test_power_ref_u32() {
let x: u32 = 2_u32.power(&3u32);
assert_eq!(x, 8);
}
}
================================================
FILE: exercises/04_traits/11_clone/Cargo.toml
================================================
[package]
name = "clone"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/11_clone/src/lib.rs
================================================
// TODO: add the necessary `Clone` implementations (and invocations)
// to get the code to compile.
pub fn summary(ticket: Ticket) -> (Ticket, Summary) {
(ticket, ticket.summary())
}
pub struct Ticket {
pub title: String,
pub description: String,
pub status: String,
}
impl Ticket {
pub fn summary(self) -> Summary {
Summary {
title: self.title,
status: self.status,
}
}
}
pub struct Summary {
pub title: String,
pub status: String,
}
================================================
FILE: exercises/04_traits/12_copy/Cargo.toml
================================================
[package]
name = "copy"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/12_copy/src/lib.rs
================================================
// TODO: implement the necessary traits to make the test compile and pass.
// You *can't* modify the test.
pub struct WrappingU32 {
value: u32,
}
impl WrappingU32 {
pub fn new(value: u32) -> Self {
Self { value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ops() {
let x = WrappingU32::new(42);
let y = WrappingU32::new(31);
let z = WrappingU32::new(u32::MAX);
assert_eq!(x + y + y + z, WrappingU32::new(103));
}
}
================================================
FILE: exercises/04_traits/13_drop/Cargo.toml
================================================
[package]
name = "drop"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/13_drop/src/lib.rs
================================================
// TODO: implement a so-called "Drop bomb": a type that panics when dropped
// unless a certain operation has been performed on it.
// You can see the expected API in the tests below.
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn test_drop_bomb() {
let bomb = DropBomb::new();
// The bomb should panic when dropped
}
#[test]
fn test_defused_drop_bomb() {
let mut bomb = DropBomb::new();
bomb.defuse();
// The bomb should not panic when dropped
// since it has been defused
}
}
================================================
FILE: exercises/04_traits/14_outro/Cargo.toml
================================================
[package]
name = "outro_03"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/04_traits/14_outro/src/lib.rs
================================================
// TODO: Define a new `SaturatingU16` type.
// It should hold a `u16` value.
// It should provide conversions from `u16`, `u8`, `&u16` and `&u8`.
// It should support addition with a right-hand side of type
// SaturatingU16, u16, &u16, and &SaturatingU16. Addition should saturate at the
// maximum value for `u16`.
// It should be possible to compare it with another `SaturatingU16` or a `u16`.
// It should be possible to print its debug representation.
//
// Tests are located in the `tests` folder—pay attention to the visibility of your types and methods.
================================================
FILE: exercises/04_traits/14_outro/tests/integration.rs
================================================
use outro_03::SaturatingU16;
#[test]
fn test_saturating_u16() {
let a: SaturatingU16 = (&10u8).into();
let b: SaturatingU16 = 5u8.into();
let c: SaturatingU16 = u16::MAX.into();
let d: SaturatingU16 = (&1u16).into();
let e = &c;
assert_eq!(a + b, SaturatingU16::from(15u16));
assert_eq!(a + c, SaturatingU16::from(u16::MAX));
assert_eq!(a + d, SaturatingU16::from(11u16));
assert_eq!(a + a, 20u16);
assert_eq!(a + 5u16, 15u16);
assert_eq!(a + e, SaturatingU16::from(u16::MAX));
}
================================================
FILE: exercises/05_ticket_v2/00_intro/Cargo.toml
================================================
[package]
name = "intro_04"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/05_ticket_v2/00_intro/src/lib.rs
================================================
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to refine the `Ticket` type!");
}
}
================================================
FILE: exercises/05_ticket_v2/01_enum/Cargo.toml
================================================
[package]
name = "enum_"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/05_ticket_v2/01_enum/src/lib.rs
================================================
// TODO: use `Status` as type for `Ticket::status`
// Adjust the signature and implementation of all other methods as necessary.
#[derive(Debug, PartialEq)]
// `derive`s are recursive: it can only derive `PartialEq` if all fields also implement `PartialEq`.
// Same holds for `Debug`. Do what you must with `Status` to make this work.
struct Ticket {
title: String,
description: String,
status: String,
}
enum Status {
// TODO: add the missing variants
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket {
title,
description,
status,
}
}
pub fn title(&self) -> &String {
&self.title
}
pub fn description(&self) -> &String {
&self.description
}
pub fn status(&self) -> &String {
&self.status
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{valid_description, valid_title};
#[test]
fn test_partial_eq() {
let title = valid_title();
let description = valid_description();
let ticket1 = Ticket {
title: title.clone(),
description: description.clone(),
status: Status::ToDo,
};
let ticket2 = Ticket {
title: title.clone(),
description: description.clone(),
status: Status::ToDo,
};
assert_eq!(ticket1, ticket2);
}
#[test]
fn test_description_not_matching() {
let title = valid_title();
let status = Status::ToDo;
let ticket1 = Ticket {
title: title.clone(),
description: "description".to_string(),
status,
};
let ticket2 = Ticket {
title: title.clone(),
description: "description2".to_string(),
status,
};
assert_ne!(ticket1, ticket2);
}
#[test]
fn test_title_not_matching() {
let description = valid_description();
let status = Status::InProgress;
let ticket1 = Ticket {
title: "title".to_string(),
description: description.clone(),
status,
};
let ticket2 = Ticket {
title: "title2".to_string(),
description: description.clone(),
status,
};
assert_ne!(ticket1, ticket2);
}
#[test]
fn test_status_not_matching() {
let title = valid_title();
let description = valid_description();
let ticket1 = Ticket {
title: title.clone(),
description: description.clone(),
status: Status::InProgress,
};
let ticket2 = Ticket {
title: title.clone(),
description: description.clone(),
status: Status::Done,
};
assert_ne!(ticket1, ticket2);
}
}
================================================
FILE: exercises/05_ticket_v2/02_match/Cargo.toml
================================================
[package]
name = "match_"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/05_ticket_v2/02_match/src/lib.rs
================================================
enum Shape {
Circle,
Square,
Rectangle,
Triangle,
Pentagon,
}
impl Shape {
// TODO: Implement the `n_sides` method using a `match`.
pub fn n_sides(&self) -> u8 {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circle() {
assert_eq!(Shape::Circle.n_sides(), 0);
}
#[test]
fn test_square() {
assert_eq!(Shape::Square.n_sides(), 4);
}
#[test]
fn test_rectangle() {
assert_eq!(Shape::Rectangle.n_sides(), 4);
}
#[test]
fn test_triangle() {
assert_eq!(Shape::Triangle.n_sides(), 3);
}
#[test]
fn test_pentagon() {
assert_eq!(Shape::Pentagon.n_sides(), 5);
}
}
================================================
FILE: exercises/05_ticket_v2/03_variants_with_data/Cargo.toml
================================================
[package]
name = "variants_with_data"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/05_ticket_v2/03_variants_with_data/src/lib.rs
================================================
// TODO: Implement `Ticket::assigned_to`.
// Return the name of the person assigned to the ticket, if the ticket is in progress.
// Panic otherwise.
#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
Ticket {
title,
description,
status,
}
}
pub fn assigned_to(&self) -> &str {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{valid_description, valid_title};
#[test]
#[should_panic(expected = "Only `In-Progress` tickets can be assigned to someone")]
fn test_todo() {
let ticket = Ticket::new(valid_title(), valid_description(), Status::ToDo);
ticket.assigned_to();
}
#[test]
#[should_panic(expected = "Only `In-Progress` tickets can be assigned to someone")]
fn test_done() {
let ticket = Ticket::new(valid_title(), valid_description(), Status::Done);
ticket.assigned_to();
}
#[test]
fn test_in_progress() {
let ticket = Ticket::new(
valid_title(),
valid_description(),
Status::InProgress {
assigned_to: "Alice".to_string(),
},
);
assert_eq!(ticket.assigned_to(), "Alice");
}
}
================================================
FILE: exercises/05_ticket_v2/04_if_let/Cargo.toml
================================================
[package]
name = "if_let"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/05_ticket_v2/04_if_let/src/lib.rs
================================================
enum Shape {
Circle { radius: f64 },
Square { border: f64 },
Rectangle { width: f64, height: f64 },
}
impl Shape {
// TODO: Implement the `radius` method using
// either an `if let` or a `let/else`.
pub fn radius(&self) -> f64 {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circle() {
let _ = Shape::Circle { radius: 1.0 }.radius();
}
#[test]
#[should_panic]
fn test_square() {
let _ = Shape::Square { border: 1.0 }.radius();
}
#[test]
#[should_panic]
fn test_rectangle() {
let _ = Shape::Rectangle {
width: 1.0,
height: 2.0,
}
.radius();
}
}
================================================
FILE: exercises/05_ticket_v2/05_nullability/Cargo.toml
================================================
[package]
name = "nullability"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/05_ticket_v2/05_nullability/src/lib.rs
================================================
// TODO: Implement `Ticket::assigned_to` using `Option` as the return type.
#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
Ticket {
title,
description,
status,
}
}
pub fn assigned_to(&self) -> Option<&String> {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{valid_description, valid_title};
#[test]
fn test_todo() {
let ticket = Ticket::new(valid_title(), valid_description(), Status::ToDo);
assert!(ticket.assigned_to().is_none());
}
#[test]
fn test_done() {
let ticket = Ticket::new(valid_title(), valid_description(), Status::Done);
assert!(ticket.assigned_to().is_none());
}
#[test]
fn test_in_progress() {
let ticket = Ticket::new(
valid_title(),
valid_description(),
Status::InProgress {
assigned_to: "Alice".to_string(),
},
);
assert_eq!(ticket.assigned_to(), Some(&"Alice".to_string()));
}
}
================================================
FILE: exercises/05_ticket_v2/06_fallibility/Cargo.toml
================================================
[package]
name = "fallibility"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/05_ticket_v2/06_fallibility/src/lib.rs
================================================
// TODO: Convert the `Ticket::new` method to return a `Result` instead of panicking.
// Use `String` as the error type.
#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
Ticket {
title,
description,
status,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
fn title_cannot_be_empty() {
let error = Ticket::new("".into(), valid_description(), Status::ToDo).unwrap_err();
assert_eq!(error, "Title cannot be empty");
}
#[test]
fn description_cannot_be_empty() {
let error = Ticket::new(valid_title(), "".into(), Status::ToDo).unwrap_err();
assert_eq!(error, "Description cannot be empty");
}
#[test]
fn title_cannot_be_longer_than_fifty_chars() {
let error =
Ticket::new(overly_long_title(), valid_description(), Status::ToDo).unwrap_err();
assert_eq!(error, "Title cannot be longer than 50 bytes");
}
#[test]
fn description_cannot_be_longer_than_500_chars() {
let error =
Ticket::new(valid_title(), overly_long_description(), Status::ToDo).unwrap_err();
assert_eq!(error, "Description cannot be longer than 500 bytes");
}
}
================================================
FILE: exercises/05_ticket_v2/07_unwrap/Cargo.toml
================================================
[package]
name = "unwrap"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/05_ticket_v2/07_unwrap/src/lib.rs
================================================
// TODO: `easy_ticket` should panic when the title is invalid.
// When the description is invalid, instead, it should use a default description:
// "Description not provided".
fn easy_ticket(title: String, description: String, status: Status) -> Ticket {
todo!()
}
#[derive(Debug, PartialEq, Clone)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq, Clone)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> Result<Ticket, String> {
if title.is_empty() {
return Err("Title cannot be empty".to_string());
}
if title.len() > 50 {
return Err("Title cannot be longer than 50 bytes".to_string());
}
if description.is_empty() {
return Err("Description cannot be empty".to_string());
}
if description.len() > 500 {
return Err("Description cannot be longer than 500 bytes".to_string());
}
Ok(Ticket {
title,
description,
status,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
#[should_panic(expected = "Title cannot be empty")]
fn title_cannot_be_empty() {
easy_ticket("".into(), valid_description(), Status::ToDo);
}
#[test]
fn template_description_is_used_if_empty() {
let ticket = easy_ticket(valid_title(), "".into(), Status::ToDo);
assert_eq!(ticket.description, "Description not provided");
}
#[test]
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
fn title_cannot_be_longer_than_fifty_chars() {
easy_ticket(overly_long_title(), valid_description(), Status::ToDo);
}
#[test]
fn template_description_is_used_if_too_long() {
let ticket = easy_ticket(valid_title(), overly_long_description(), Status::ToDo);
assert_eq!(ticket.description, "Description not provided");
}
}
================================================
FILE: exercises/05_ticket_v2/08_error_enums/Cargo.toml
================================================
[package]
name = "error_enums"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/05_ticket_v2/08_error_enums/src/lib.rs
================================================
// TODO: Use two variants, one for a title error and one for a description error.
// Each variant should contain a string with the explanation of what went wrong exactly.
// You'll have to update the implementation of `Ticket::new` as well.
enum TicketNewError {}
// TODO: `easy_ticket` should panic when the title is invalid, using the error message
// stored inside the relevant variant of the `TicketNewError` enum.
// When the description is invalid, instead, it should use a default description:
// "Description not provided".
fn easy_ticket(title: String, description: String, status: Status) -> Ticket {
todo!()
}
#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq, Clone)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(
title: String,
description: String,
status: Status,
) -> Result<Ticket, TicketNewError> {
if title.is_empty() {
return Err("Title cannot be empty".to_string());
}
if title.len() > 50 {
return Err("Title cannot be longer than 50 bytes".to_string());
}
if description.is_empty() {
return Err("Description cannot be empty".to_string());
}
if description.len() > 500 {
return Err("Description cannot be longer than 500 bytes".to_string());
}
Ok(Ticket {
title,
description,
status,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
#[should_panic(expected = "Title cannot be empty")]
fn title_cannot_be_empty() {
easy_ticket("".into(), valid_description(), Status::ToDo);
}
#[test]
fn template_description_is_used_if_empty() {
let ticket = easy_ticket(valid_title(), "".into(), Status::ToDo);
assert_eq!(ticket.description, "Description not provided");
}
#[test]
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
fn title_cannot_be_longer_than_fifty_chars() {
easy_ticket(overly_long_title(), valid_description(), Status::ToDo);
}
#[test]
fn template_description_is_used_if_too_long() {
let ticket = easy_ticket(valid_title(), overly_long_description(), Status::ToDo);
assert_eq!(ticket.description, "Description not provided");
}
}
================================================
FILE: exercises/05_ticket_v2/09_error_trait/Cargo.toml
================================================
[package]
name = "error_trait"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
static_assertions = "1.1.0"
================================================
FILE: exercises/05_ticket_v2/09_error_trait/src/lib.rs
================================================
// TODO: Implement `Debug`, `Display` and `Error` for the `TicketNewError` enum.
// When implementing `Display`, you may want to use the `write!` macro from Rust's standard library.
// The docs for the `std::fmt` module are a good place to start and look for examples:
// https://doc.rust-lang.org/std/fmt/index.html#write
enum TicketNewError {
TitleError(String),
DescriptionError(String),
}
// TODO: `easy_ticket` should panic when the title is invalid, using the error message
// stored inside the relevant variant of the `TicketNewError` enum.
// When the description is invalid, instead, it should use a default description:
// "Description not provided".
fn easy_ticket(title: String, description: String, status: Status) -> Ticket {
todo!()
}
#[derive(Debug, PartialEq, Clone)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq, Clone)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(
title: String,
description: String,
status: Status,
) -> Result<Ticket, TicketNewError> {
if title.is_empty() {
return Err(TicketNewError::TitleError(
"Title cannot be empty".to_string(),
));
}
if title.len() > 50 {
return Err(TicketNewError::TitleError(
"Title cannot be longer than 50 bytes".to_string(),
));
}
if description.is_empty() {
return Err(TicketNewError::DescriptionError(
"Description cannot be empty".to_string(),
));
}
if description.len() > 500 {
return Err(TicketNewError::DescriptionError(
"Description cannot be longer than 500 bytes".to_string(),
));
}
Ok(Ticket {
title,
description,
status,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
use static_assertions::assert_impl_one;
#[test]
#[should_panic(expected = "Title cannot be empty")]
fn title_cannot_be_empty() {
easy_ticket("".into(), valid_description(), Status::ToDo);
}
#[test]
fn template_description_is_used_if_empty() {
let ticket = easy_ticket(valid_title(), "".into(), Status::ToDo);
assert_eq!(ticket.description, "Description not provided");
}
#[test]
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
fn title_cannot_be_longer_than_fifty_chars() {
easy_ticket(overly_long_title(), valid_description(), Status::ToDo);
}
#[test]
fn template_description_is_used_if_too_long() {
let ticket = easy_ticket(valid_title(), overly_long_description(), Status::ToDo);
assert_eq!(ticket.description, "Description not provided");
}
#[test]
fn display_is_correctly_implemented() {
let ticket = Ticket::new("".into(), valid_description(), Status::ToDo);
assert_eq!(format!("{}", ticket.unwrap_err()), "Title cannot be empty");
}
assert_impl_one!(TicketNewError: std::error::Error);
}
================================================
FILE: exercises/05_ticket_v2/10_packages/Cargo.toml
================================================
[package]
name = "packages"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/05_ticket_v2/10_packages/src/main.rs
================================================
// This is a `main.rs` file, therefore `cargo` interprets this as the root of a binary target.
// TODO: fix this broken import. Create a new library target in the `src` directory.
// The library target should expose a public function named `hello_world` that takes no arguments
// and returns nothing.
use packages::hello_world;
// This is the entrypoint of the binary.
fn main() {
hello_world();
}
================================================
FILE: exercises/05_ticket_v2/11_dependencies/Cargo.toml
================================================
[package]
name = "deps"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/05_ticket_v2/11_dependencies/src/lib.rs
================================================
// TODO: Add `anyhow` as a dependency of this project.
// Don't touch this import!
// When you import a type (`Error`) from a dependency, the import path must start
// with the crate name (`anyhow`, in this case).
use anyhow::Error;
================================================
FILE: exercises/05_ticket_v2/12_thiserror/Cargo.toml
================================================
[package]
name = "thiserror_"
version = "0.1.0"
edition = "2021"
[dependencies]
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/05_ticket_v2/12_thiserror/src/lib.rs
================================================
// TODO: Implement the `Error` trait for `TicketNewError` using `thiserror`.
// We've changed the enum variants to be more specific, thus removing the need for storing
// a `String` field into each variant.
// You'll also have to add `thiserror` as a dependency in the `Cargo.toml` file.
enum TicketNewError {
TitleCannotBeEmpty,
TitleTooLong,
DescriptionCannotBeEmpty,
DescriptionTooLong,
}
#[derive(Debug, PartialEq, Clone)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq, Clone)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(
title: String,
description: String,
status: Status,
) -> Result<Ticket, TicketNewError> {
if title.is_empty() {
return Err(TicketNewError::TitleCannotBeEmpty);
}
if title.len() > 50 {
return Err(TicketNewError::TitleTooLong);
}
if description.is_empty() {
return Err(TicketNewError::DescriptionCannotBeEmpty);
}
if description.len() > 500 {
return Err(TicketNewError::DescriptionTooLong);
}
Ok(Ticket {
title,
description,
status,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
fn title_cannot_be_empty() {
let err = Ticket::new("".into(), valid_description(), Status::ToDo).unwrap_err();
assert_eq!(err.to_string(), "Title cannot be empty");
}
#[test]
fn description_cannot_be_empty() {
let err = Ticket::new(valid_title(), "".into(), Status::ToDo).unwrap_err();
assert_eq!(err.to_string(), "Description cannot be empty");
}
#[test]
fn title_cannot_be_longer_than_fifty_chars() {
let err = Ticket::new(overly_long_title(), valid_description(), Status::ToDo).unwrap_err();
assert_eq!(err.to_string(), "Title cannot be longer than 50 bytes");
}
#[test]
fn description_cannot_be_too_long() {
let err = Ticket::new(valid_title(), overly_long_description(), Status::ToDo).unwrap_err();
assert_eq!(
err.to_string(),
"Description cannot be longer than 500 bytes"
);
}
}
================================================
FILE: exercises/05_ticket_v2/13_try_from/Cargo.toml
================================================
[package]
name = "tryfrom"
version = "0.1.0"
edition = "2021"
[dependencies]
================================================
FILE: exercises/05_ticket_v2/13_try_from/src/lib.rs
================================================
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for `Status`.
// The parsing should be case-insensitive.
#[derive(Debug, PartialEq, Clone)]
enum Status {
ToDo,
InProgress,
Done,
}
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
#[test]
fn test_try_from_string() {
let status = Status::try_from("ToDO".to_string()).unwrap();
assert_eq!(status, Status::ToDo);
let status = Status::try_from("inproGress".to_string()).unwrap();
assert_eq!(status, Status::InProgress);
let status = Status::try_from("Done".to_string()).unwrap();
assert_eq!(status, Status::Done);
}
#[test]
fn test_try_from_str() {
let status = Status::try_from("todo").unwrap();
assert_eq!(status, Status::ToDo);
let status = Status::try_from("inprogress").unwrap();
assert_eq!(status, Status::InProgress);
let status = Status::try_from("done").unwrap();
assert_eq!(status, Status::Done);
}
}
================================================
FILE: exercises/05_ticket_v2/14_source/Cargo.toml
================================================
[package]
name = "source"
version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.69"
[dev-dependencies]
common = { path = "../../../helpers/common" }
================================================
FILE: exercises/05_ticket_v2/14_source/src/lib.rs
================================================
use crate::status::Status;
// We've seen how to declare modules in one of the earliest exercises, but
// we haven't seen how to extract them into separate files.
// Let's fix that now!
//
// In the simplest case, when the extracted module is a single file, it is enough to
// create a new file with the same name as the module and move the module content there.
// The module file should be placed in the same directory as the file that declares the module.
// In this case, `src/lib.rs`, thus `status.rs` should be placed in the `src` directory.
mod status;
// TODO: Add a new error variant to `TicketNewError` for when the status string is invalid.
// When calling `source` on an error of that variant, it should return a `ParseStatusError` rather than `None`.
#[derive(Debug, thiserror::Error)]
pub enum TicketNewError {
#[error("Title cannot be empty")]
TitleCannotBeEmpty,
#[error("Title cannot be longer than 50 bytes")]
TitleTooLong,
#[error("Description cannot be empty")]
DescriptionCannotBeEmpty,
#[error("Description cannot be longer than 500 bytes")]
DescriptionTooLong,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Ticket {
title: String,
description: String,
status: Status,
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Result<Self, TicketNewError> {
if title.is_empty() {
return Err(TicketNewError::TitleCannotBeEmpty);
}
if title.len() > 50 {
return Err(TicketNewError::TitleTooLong);
}
if description.is_empty() {
return Err(TicketNewError::DescriptionCannotBeEmpty);
}
if description.len() > 500 {
return Err(TicketNewError::DescriptionTooLong);
}
// TODO: Parse the status string into a `Status` enum.
Ok(Ticket {
title,
description,
status,
})
}
}
#[cfg(test)]
mod tests {
use common::{valid_description, valid_title};
use std::error::Error;
use super::*;
#[test]
fn invalid_status() {
let err = Ticket::new(valid_title(), valid_description(), "invalid".into()).unwrap_err();
assert_eq!(
err.to_string(),
"`invalid` is not a valid status. Use one of: ToDo, InProgress, Done"
);
assert!(err.source().is_some());
}
}
================================================
FILE: exercises/05_ticket_v2/14_source/src/status.rs
================================================
#[derive(Debug, PartialEq, Clone)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TryFrom<String> for Status {
type Error = ParseStatusError;
fn try_from(value: String) -> Result<Self, Self::Error> {
let value = value.to_lowercase();
match value.as_str() {
"todo" => Ok(Status::ToDo),
"inprogress" => Ok(Status::InProgress),
"done" => Ok(Status::Done),
_ => Err(ParseStatusError {
invalid_status: value,
}),
}
}
}
#[derive(Debug, thiserror::Error)]
#[error("`{invalid_status}` is not a valid status. Use one of: ToDo, InProgress, Done")]
pub struct ParseStatusError {
invalid_status: String,
}
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
#[test]
fn test_try_from_string() {
let status = Status::try_from("ToDO".to_string()).unwrap();
assert_eq!(status, Status::ToDo);
let status = Status::try_from("inproGress".to_string()).unwrap();
assert_eq!(status, Status::InProgress);
let status = Status::try_from("Done".to_string()).unwrap();
assert_eq!(status, Status::Done);
}
}
================================================
FILE: exercises/05_ticket_v2/15_outro/Cargo.toml
================================================
[package]
name = "outro_04"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/05_ticket_v2/15_outro/src/description.rs
================================================
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `TicketDescription` type,
// enforcing that the description is not empty and is not longer than 500 bytes.
// Implement the traits required to make the tests pass too.
pub struct TicketDescription(String);
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
#[test]
fn test_try_from_string() {
let description = TicketDescription::try_from("A description".to_string()).unwrap();
assert_eq!(description.0, "A description");
}
#[test]
fn test_try_from_empty_string() {
let err = TicketDescription::try_from("".to_string()).unwrap_err();
assert_eq!(err.to_string(), "The description cannot be empty");
}
#[test]
fn test_try_from_long_string() {
let description = "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.".to_string();
let err = TicketDescription::try_from(description).unwrap_err();
assert_eq!(
err.to_string(),
"The description cannot be longer than 500 bytes"
);
}
#[test]
fn test_try_from_str() {
let description = TicketDescription::try_from("A description").unwrap();
assert_eq!(description.0, "A description");
}
}
================================================
FILE: exercises/05_ticket_v2/15_outro/src/lib.rs
================================================
// TODO: you have something to do in each of the modules in this crate!
mod description;
mod status;
mod title;
// A common pattern in Rust is to split code into multiple (private) modules
// and then re-export the public parts of those modules at the root of the crate.
//
// This hides the internal structure of the crate from your users, while still
// allowing you to organize your code however you like.
pub use description::TicketDescription;
pub use status::Status;
pub use title::TicketTitle;
#[derive(Debug, PartialEq, Clone)]
// We no longer need to make the fields private!
// Since each field encapsulates its own validation logic, there is no risk of
// a user of `Ticket` modifying the fields in a way that would break the
// invariants of the struct.
//
// Careful though: if you had any invariants that spanned multiple fields, you
// would need to ensure that those invariants are still maintained and go back
// to making the fields private.
pub struct Ticket {
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
================================================
FILE: exercises/05_ticket_v2/15_outro/src/status.rs
================================================
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `Status` enum.
// The parsing should be case-insensitive.
pub enum Status {
ToDo,
InProgress,
Done,
}
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
#[test]
fn test_try_from_string() {
let status = Status::try_from("ToDO".to_string()).unwrap();
assert_eq!(status, Status::ToDo);
let status = Status::try_from("inproGress".to_string()).unwrap();
assert_eq!(status, Status::InProgress);
let status = Status::try_from("Done".to_string()).unwrap();
assert_eq!(status, Status::Done);
}
#[test]
fn test_try_from_str() {
let status = Status::try_from("ToDO").unwrap();
assert_eq!(status, Status::ToDo);
let status = Status::try_from("inproGress").unwrap();
assert_eq!(status, Status::InProgress);
let status = Status::try_from("Done").unwrap();
assert_eq!(status, Status::Done);
}
#[test]
fn test_try_from_invalid() {
let status = Status::try_from("Invalid");
assert!(status.is_err());
}
}
================================================
FILE: exercises/05_ticket_v2/15_outro/src/title.rs
================================================
// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `TicketTitle` type,
// enforcing that the title is not empty and is not longer than 50 bytes.
// Implement the traits required to make the tests pass too.
pub struct TicketTitle(String);
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
#[test]
fn test_try_from_string() {
let title = TicketTitle::try_from("A title".to_string()).unwrap();
assert_eq!(title.0, "A title");
}
#[test]
fn test_try_from_empty_string() {
let err = TicketTitle::try_from("".to_string()).unwrap_err();
assert_eq!(err.to_string(), "The title cannot be empty");
}
#[test]
fn test_try_from_long_string() {
let title =
"A title that's definitely longer than what should be allowed in a development ticket"
.to_string();
let err = TicketTitle::try_from(title).unwrap_err();
assert_eq!(err.to_string(), "The title cannot be longer than 50 bytes");
}
#[test]
fn test_try_from_str() {
let title = TicketTitle::try_from("A title").unwrap();
assert_eq!(title.0, "A title");
}
}
================================================
FILE: exercises/06_ticket_management/00_intro/Cargo.toml
================================================
[package]
name = "intro_05"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/06_ticket_management/00_intro/src/lib.rs
================================================
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to build a ticket management system!");
}
}
================================================
FILE: exercises/06_ticket_management/01_arrays/Cargo.toml
================================================
[package]
name = "arrays"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/06_ticket_management/01_arrays/src/lib.rs
================================================
// TODO: Flesh out the `WeekTemperatures` struct and its method implementations to pass the tests.
pub struct WeekTemperatures {
// TODO
}
pub enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
impl WeekTemperatures {
pub fn new() -> Self {
todo!()
}
pub fn get_temperature(&self, day: Weekday) -> Option<i32> {
todo!()
}
pub fn set_temperature(&mut self, day: Weekday, temperature: i32) {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_temperature() {
let mut week_temperatures = WeekTemperatures::new();
assert_eq!(week_temperatures.get_temperature(Weekday::Monday), None);
assert_eq!(week_temperatures.get_temperature(Weekday::Tuesday), None);
assert_eq!(week_temperatures.get_temperature(Weekday::Wednesday), None);
assert_eq!(week_temperatures.get_temperature(Weekday::Thursday), None);
assert_eq!(week_temperatures.get_temperature(Weekday::Friday), None);
assert_eq!(week_temperatures.get_temperature(Weekday::Saturday), None);
assert_eq!(week_temperatures.get_temperature(Weekday::Sunday), None);
week_temperatures.set_temperature(Weekday::Monday, 20);
assert_eq!(week_temperatures.get_temperature(Weekday::Monday), Some(20));
week_temperatures.set_temperature(Weekday::Monday, 25);
assert_eq!(week_temperatures.get_temperature(Weekday::Monday), Some(25));
week_temperatures.set_temperature(Weekday::Tuesday, 30);
week_temperatures.set_temperature(Weekday::Wednesday, 35);
week_temperatures.set_temperature(Weekday::Thursday, 40);
week_temperatures.set_temperature(Weekday::Friday, 45);
week_temperatures.set_temperature(Weekday::Saturday, 50);
week_temperatures.set_temperature(Weekday::Sunday, 55);
assert_eq!(week_temperatures.get_temperature(Weekday::Monday), Some(25));
assert_eq!(
week_temperatures.get_temperature(Weekday::Tuesday),
Some(30)
);
assert_eq!(
week_temperatures.get_temperature(Weekday::Wednesday),
Some(35)
);
assert_eq!(
week_temperatures.get_temperature(Weekday::Thursday),
Some(40)
);
assert_eq!(week_temperatures.get_temperature(Weekday::Friday), Some(45));
assert_eq!(
week_temperatures.get_temperature(Weekday::Saturday),
Some(50)
);
assert_eq!(week_temperatures.get_temperature(Weekday::Sunday), Some(55));
}
}
================================================
FILE: exercises/06_ticket_management/02_vec/Cargo.toml
================================================
[package]
name = "vec"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/06_ticket_management/02_vec/src/lib.rs
================================================
// Given a number `n`, return the `n+1`th number in the Fibonacci sequence.
//
// The Fibonacci sequence is defined as follows:
//
// - The first number of the sequence is 0.
// - The second number of the sequence is 1.
// - Every subsequent number is the sum of the two preceding numbers.
//
// So the sequence goes: 0, 1, 1, 2, 3, 5, 8, 13, 21, and so on.
//
// We expect `fibonacci(0)` to return `0`, `fibonacci(1)` to return `1`,
// `fibonacci(2)` to return `1`, and so on.
pub fn fibonacci(n: u32) -> u32 {
// TODO: implement the `fibonacci` function
//
// Hint: use a `Vec` to memoize the results you have already calculated
// so that you don't have to recalculate them several times.
todo!()
}
#[cfg(test)]
mod tests {
use crate::fibonacci;
#[test]
fn first() {
assert_eq!(fibonacci(0), 0);
}
#[test]
fn second() {
assert_eq!(fibonacci(1), 1);
}
#[test]
fn third() {
assert_eq!(fibonacci(2), 1);
}
#[test]
fn tenth() {
assert_eq!(fibonacci(10), 55);
}
#[test]
fn thirtieth() {
assert_eq!(fibonacci(30), 832040);
}
}
================================================
FILE: exercises/06_ticket_management/03_resizing/Cargo.toml
================================================
[package]
name = "resizing"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/06_ticket_management/03_resizing/src/lib.rs
================================================
#[cfg(test)]
mod tests {
#[test]
fn resizing() {
let mut v = Vec::with_capacity(2);
v.push(1);
v.push(2); // max capacity reached
assert_eq!(v.capacity(), 2);
v.push(3); // beyond capacity, needs to resize
// Can you guess what the new capacity will be?
// Beware that the standard library makes no guarantees about the
// algorithm used to resize the vector, so this may change in the future.
assert_eq!(v.capacity(), todo!());
}
}
================================================
FILE: exercises/06_ticket_management/04_iterators/Cargo.toml
================================================
[package]
name = "iterators"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/04_iterators/src/lib.rs
================================================
use ticket_fields::{TicketDescription, TicketTitle};
// TODO: Let's start sketching our ticket store!
// First task: implement `IntoIterator` on `TicketStore` to allow iterating over all the tickets
// it contains using a `for` loop.
//
// Hint: you shouldn't have to implement the `Iterator` trait in this case.
// You want to *delegate* the iteration to the `Vec<Ticket>` field in `TicketStore`.
// Look at the standard library documentation for `Vec` to find the right type
// to return from `into_iter`.
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
pub fn add_ticket(&mut self, ticket: Ticket) {
self.tickets.push(ticket);
}
}
#[cfg(test)]
mod tests {
use super::*;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn add_ticket() {
let mut store = TicketStore::new();
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::ToDo,
};
store.add_ticket(ticket);
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::InProgress,
};
store.add_ticket(ticket);
let tickets: Vec<_> = store.clone().into_iter().collect();
assert_eq!(tickets, store.tickets);
}
}
================================================
FILE: exercises/06_ticket_management/05_iter/Cargo.toml
================================================
[package]
name = "iter"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/05_iter/src/lib.rs
================================================
use ticket_fields::{TicketDescription, TicketTitle};
// TODO: Provide an `iter` method that returns an iterator over `&Ticket` items.
//
// Hint: just like in the previous exercise, you want to delegate the iteration to
// the `Vec<Ticket>` field in `TicketStore`. Look at the standard library documentation
// for `Vec` to find the right type to return from `iter`.
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Ticket {
title: TicketTitle,
description: TicketDescription,
status: Status,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
pub fn add_ticket(&mut self, ticket: Ticket) {
self.tickets.push(ticket);
}
}
#[cfg(test)]
mod tests {
use super::*;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn add_ticket() {
let mut store = TicketStore::new();
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::ToDo,
};
store.add_ticket(ticket);
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::InProgress,
};
store.add_ticket(ticket);
let tickets: Vec<&Ticket> = store.iter().collect();
let tickets2: Vec<&Ticket> = store.iter().collect();
assert_eq!(tickets, tickets2);
}
}
================================================
FILE: exercises/06_ticket_management/06_lifetimes/Cargo.toml
================================================
[package]
name = "lifetime"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/06_lifetimes/src/lib.rs
================================================
use ticket_fields::{TicketDescription, TicketTitle};
// TODO: Implement the `IntoIterator` trait for `&TicketStore` so that the test compiles and passes.
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
pub fn add_ticket(&mut self, ticket: Ticket) {
self.tickets.push(ticket);
}
pub fn iter(&self) -> std::slice::Iter<Ticket> {
self.tickets.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn add_ticket() {
let mut store = TicketStore::new();
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::ToDo,
};
store.add_ticket(ticket);
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::InProgress,
};
store.add_ticket(ticket);
let tickets: Vec<&Ticket> = store.iter().collect();
let tickets2: Vec<&Ticket> = (&store).into_iter().collect();
assert_eq!(tickets, tickets2);
}
}
================================================
FILE: exercises/06_ticket_management/07_combinators/Cargo.toml
================================================
[package]
name = "combinators"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/07_combinators/src/lib.rs
================================================
// TODO: Implement the `to_dos` method. It must return a `Vec` of references to the tickets
// in `TicketStore` with status set to `Status::ToDo`.
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
pub fn add_ticket(&mut self, ticket: Ticket) {
self.tickets.push(ticket);
}
}
#[cfg(test)]
mod tests {
use super::*;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn todos() {
let mut store = TicketStore::new();
let todo = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::ToDo,
};
store.add_ticket(todo.clone());
let ticket = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::InProgress,
};
store.add_ticket(ticket);
let todos: Vec<&Ticket> = store.to_dos();
assert_eq!(todos.len(), 1);
assert_eq!(todos[0], &todo);
}
}
================================================
FILE: exercises/06_ticket_management/08_impl_trait/Cargo.toml
================================================
[package]
name = "impl_trait"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/08_impl_trait/src/lib.rs
================================================
// TODO: Implement the `in_progress` method. It must return an iterator over the tickets in
// `TicketStore` with status set to `Status::InProgress`.
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
pub fn add_ticket(&mut self, ticket: Ticket) {
self.tickets.push(ticket);
}
}
#[cfg(test)]
mod tests {
use super::*;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn in_progress() {
let mut store = TicketStore::new();
let todo = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::ToDo,
};
store.add_ticket(todo);
let in_progress = Ticket {
title: ticket_title(),
description: ticket_description(),
status: Status::InProgress,
};
store.add_ticket(in_progress.clone());
let in_progress_tickets: Vec<&Ticket> = store.in_progress().collect();
assert_eq!(in_progress_tickets.len(), 1);
assert_eq!(in_progress_tickets[0], &in_progress);
}
}
================================================
FILE: exercises/06_ticket_management/09_impl_trait_2/Cargo.toml
================================================
[package]
name = "impl_trait_2"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/09_impl_trait_2/src/lib.rs
================================================
// TODO: Rework the signature of `TicketStore::add_ticket` to use a generic type parameter rather
// than `impl Trait` syntax.
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
// Using `Into<Ticket>` as the type parameter for `ticket` allows the method to accept any type
// that can be infallibly converted into a `Ticket`.
// This can make it nicer to use the method, as it removes the syntax noise of `.into()`
// from the calling site. It can worsen the quality of the compiler error messages, though.
pub fn add_ticket(&mut self, ticket: impl Into<Ticket>) {
self.tickets.push(ticket.into());
}
}
#[cfg(test)]
mod tests {
use super::*;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
impl From<TicketDraft> for Ticket {
fn from(draft: TicketDraft) -> Self {
Self {
title: draft.title,
description: draft.description,
status: Status::ToDo,
}
}
}
#[test]
fn generic_add() {
let mut store = TicketStore::new();
// This won't compile if `add_ticket` uses `impl Trait` syntax in argument position.
store.add_ticket::<TicketDraft>(TicketDraft {
title: ticket_title(),
description: ticket_description(),
});
}
}
================================================
FILE: exercises/06_ticket_management/10_slices/Cargo.toml
================================================
[package]
name = "slice"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/06_ticket_management/10_slices/src/lib.rs
================================================
// TODO: Define a function named `sum` that takes a reference to a slice of `u32` and returns the sum of all
// elements in the slice.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let v = vec![];
assert_eq!(sum(&v), 0);
}
#[test]
fn one_element() {
let v = vec![1];
assert_eq!(sum(&v), 1);
}
#[test]
fn multiple_elements() {
let v = vec![1, 2, 3, 4, 5];
assert_eq!(sum(&v), 15);
}
#[test]
fn array_slice() {
let v = [1, 2, 3, 4, 5];
assert_eq!(sum(&v), 15);
}
}
================================================
FILE: exercises/06_ticket_management/11_mutable_slices/Cargo.toml
================================================
[package]
name = "mut_slice"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/06_ticket_management/11_mutable_slices/src/lib.rs
================================================
// TODO: Define a function named `squared` that raises all `i32`s within a slice to the power of 2.
// The slice should be modified in place.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let mut s = vec![];
squared(&mut s);
assert_eq!(s, vec![]);
}
#[test]
fn one() {
let mut s = [2];
squared(&mut s);
assert_eq!(s, [4]);
}
#[test]
fn multiple() {
let mut s = vec![2, 4];
squared(&mut s);
assert_eq!(s, vec![4, 16]);
}
}
================================================
FILE: exercises/06_ticket_management/12_two_states/Cargo.toml
================================================
[package]
name = "two_states"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/12_two_states/src/lib.rs
================================================
// TODO: Update `add_ticket`'s signature: it should take a `TicketDraft` as input
// and return a `TicketId` as output.
// Each ticket should have a unique id, generated by `TicketStore`.
// Feel free to modify `TicketStore` fields, if needed.
//
// You also need to add a `get` method that takes as input a `TicketId`
// and returns an `Option<&Ticket>`.
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TicketId(u64);
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
}
}
pub fn add_ticket(&mut self, ticket: Ticket) {
self.tickets.push(ticket);
}
}
#[cfg(test)]
mod tests {
use crate::{Status, TicketDraft, TicketStore};
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let mut store = TicketStore::new();
let draft1 = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id1 = store.add_ticket(draft1.clone());
let ticket1 = store.get(id1).unwrap();
assert_eq!(draft1.title, ticket1.title);
assert_eq!(draft1.description, ticket1.description);
assert_eq!(ticket1.status, Status::ToDo);
let draft2 = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id2 = store.add_ticket(draft2);
let ticket2 = store.get(id2).unwrap();
assert_ne!(id1, id2);
}
}
================================================
FILE: exercises/06_ticket_management/13_index/Cargo.toml
================================================
[package]
name = "index"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/13_index/src/lib.rs
================================================
// TODO: Implement `Index<&TicketId>` and `Index<TicketId>` for `TicketStore`.
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
counter: u64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TicketId(u64);
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
self.tickets.push(ticket);
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
self.tickets.iter().find(|&t| t.id == id)
}
}
#[cfg(test)]
mod tests {
use crate::{Status, TicketDraft, TicketStore};
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let mut store = TicketStore::new();
let draft1 = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id1 = store.add_ticket(draft1.clone());
let ticket1 = &store[id1];
assert_eq!(draft1.title, ticket1.title);
assert_eq!(draft1.description, ticket1.description);
assert_eq!(ticket1.status, Status::ToDo);
let draft2 = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id2 = store.add_ticket(draft2);
let ticket2 = &store[&id2];
assert_ne!(id1, id2);
}
}
================================================
FILE: exercises/06_ticket_management/14_index_mut/Cargo.toml
================================================
[package]
name = "index_mut"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/14_index_mut/src/lib.rs
================================================
// TODO: Implement `IndexMut<&TicketId>` and `IndexMut<TicketId>` for `TicketStore`.
use std::ops::Index;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: Vec<Ticket>,
counter: u64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TicketId(u64);
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: Vec::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
self.tickets.push(ticket);
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
self.tickets.iter().find(|&t| t.id == id)
}
}
impl Index<TicketId> for TicketStore {
type Output = Ticket;
fn index(&self, index: TicketId) -> &Self::Output {
self.get(index).unwrap()
}
}
impl Index<&TicketId> for TicketStore {
type Output = Ticket;
fn index(&self, index: &TicketId) -> &Self::Output {
&self[*index]
}
}
#[cfg(test)]
mod tests {
use crate::{Status, TicketDraft, TicketStore};
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let mut store = TicketStore::new();
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id = store.add_ticket(draft.clone());
let ticket = &store[id];
assert_eq!(draft.title, ticket.title);
assert_eq!(draft.description, ticket.description);
assert_eq!(ticket.status, Status::ToDo);
let ticket = &mut store[id];
ticket.status = Status::InProgress;
let ticket = &store[id];
assert_eq!(ticket.status, Status::InProgress);
let ticket = &mut store[&id];
ticket.status = Status::Done;
let ticket = &store[id];
assert_eq!(ticket.status, Status::Done);
}
}
================================================
FILE: exercises/06_ticket_management/15_hashmap/Cargo.toml
================================================
[package]
name = "hashmap"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/15_hashmap/src/lib.rs
================================================
// TODO: Replace `todo!()`s with the correct implementation.
// Implement additional traits on `TicketId` if needed.
use std::collections::HashMap;
use std::ops::{Index, IndexMut};
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: HashMap<TicketId, Ticket>,
counter: u64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TicketId(u64);
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: todo!(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
todo!();
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
todo!()
}
pub fn get_mut(&mut self, id: TicketId) -> Option<&mut Ticket> {
todo!()
}
}
impl Index<TicketId> for TicketStore {
type Output = Ticket;
fn index(&self, index: TicketId) -> &Self::Output {
self.get(index).unwrap()
}
}
impl Index<&TicketId> for TicketStore {
type Output = Ticket;
fn index(&self, index: &TicketId) -> &Self::Output {
&self[*index]
}
}
impl IndexMut<TicketId> for TicketStore {
fn index_mut(&mut self, index: TicketId) -> &mut Self::Output {
self.get_mut(index).unwrap()
}
}
impl IndexMut<&TicketId> for TicketStore {
fn index_mut(&mut self, index: &TicketId) -> &mut Self::Output {
&mut self[*index]
}
}
#[cfg(test)]
mod tests {
use crate::{Status, TicketDraft, TicketStore};
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let mut store = TicketStore::new();
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id = store.add_ticket(draft.clone());
let ticket = &store[id];
assert_eq!(draft.title, ticket.title);
assert_eq!(draft.description, ticket.description);
assert_eq!(ticket.status, Status::ToDo);
let ticket = &mut store[id];
ticket.status = Status::InProgress;
let ticket = &store[id];
assert_eq!(ticket.status, Status::InProgress);
}
}
================================================
FILE: exercises/06_ticket_management/16_btreemap/Cargo.toml
================================================
[package]
name = "btreemap"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/06_ticket_management/16_btreemap/src/lib.rs
================================================
// TODO: Replace `todo!()`s with the correct implementation.
// Implement `IntoIterator` for `&TicketStore`. The iterator should yield immutable
// references to the tickets, ordered by their `TicketId`.
// Implement additional traits on `TicketId` if needed.
use std::collections::BTreeMap;
use std::ops::{Index, IndexMut};
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Ticket>,
counter: u64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TicketId(u64);
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: todo!(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
todo!();
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
todo!()
}
pub fn get_mut(&mut self, id: TicketId) -> Option<&mut Ticket> {
todo!()
}
}
impl Index<TicketId> for TicketStore {
type Output = Ticket;
fn index(&self, index: TicketId) -> &Self::Output {
self.get(index).unwrap()
}
}
impl Index<&TicketId> for TicketStore {
type Output = Ticket;
fn index(&self, index: &TicketId) -> &Self::Output {
&self[*index]
}
}
impl IndexMut<TicketId> for TicketStore {
fn index_mut(&mut self, index: TicketId) -> &mut Self::Output {
self.get_mut(index).unwrap()
}
}
impl IndexMut<&TicketId> for TicketStore {
fn index_mut(&mut self, index: &TicketId) -> &mut Self::Output {
&mut self[*index]
}
}
#[cfg(test)]
mod tests {
use crate::{Status, TicketDraft, TicketId, TicketStore};
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let mut store = TicketStore::new();
let n_tickets = 5;
for i in 0..n_tickets {
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let id = store.add_ticket(draft.clone());
let ticket = &store[id];
assert_eq!(draft.title, ticket.title);
assert_eq!(draft.description, ticket.description);
assert_eq!(ticket.status, Status::ToDo);
let ticket = &mut store[id];
ticket.status = Status::InProgress;
let ticket = &store[id];
assert_eq!(ticket.status, Status::InProgress);
}
let ids: Vec<TicketId> = (&store).into_iter().map(|t| t.id).collect();
let sorted_ids = {
let mut v = ids.clone();
v.sort();
v
};
assert_eq!(ids, sorted_ids);
}
}
================================================
FILE: exercises/07_threads/00_intro/Cargo.toml
================================================
[package]
name = "intro_07"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/07_threads/00_intro/src/lib.rs
================================================
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to _!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(
intro(),
"I'm ready to build a concurrent ticket management system!"
);
}
}
================================================
FILE: exercises/07_threads/01_threads/Cargo.toml
================================================
[package]
name = "threads"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/07_threads/01_threads/src/lib.rs
================================================
// TODO: implement a multi-threaded version of the `sum` function
// using `spawn` and `join`.
// Given a vector of integers, split the vector into two halves and
// sum each half in a separate thread.
// Caveat: We can't test *how* the function is implemented,
// we can only verify that it produces the correct result.
// You _could_ pass this test by just returning `v.iter().sum()`,
// but that would defeat the purpose of the exercise.
//
// Hint: you won't be able to get the spawned threads to _borrow_
// slices of the vector directly. You'll need to allocate new
// vectors for each half of the original vector. We'll see why
// this is necessary in the next exercise.
use std::thread;
pub fn sum(v: Vec<i32>) -> i32 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
assert_eq!(sum(vec![]), 0);
}
#[test]
fn one() {
assert_eq!(sum(vec![1]), 1);
}
#[test]
fn five() {
assert_eq!(sum(vec![1, 2, 3, 4, 5]), 15);
}
#[test]
fn nine() {
assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]), 45);
}
#[test]
fn ten() {
assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 55);
}
}
================================================
FILE: exercises/07_threads/02_static/Cargo.toml
================================================
[package]
name = "static"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/07_threads/02_static/src/lib.rs
================================================
// TODO: Given a static slice of integers, split the slice into two halves and
// sum each half in a separate thread.
// Do not allocate any additional memory!
use std::thread;
pub fn sum(slice: &'static [i32]) -> i32 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
static ARRAY: [i32; 0] = [];
assert_eq!(sum(&ARRAY), 0);
}
#[test]
fn one() {
static ARRAY: [i32; 1] = [1];
assert_eq!(sum(&ARRAY), 1);
}
#[test]
fn five() {
static ARRAY: [i32; 5] = [1, 2, 3, 4, 5];
assert_eq!(sum(&ARRAY), 15);
}
#[test]
fn nine() {
static ARRAY: [i32; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(sum(&ARRAY), 45);
}
#[test]
fn ten() {
static ARRAY: [i32; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assert_eq!(sum(&ARRAY), 55);
}
}
================================================
FILE: exercises/07_threads/03_leak/Cargo.toml
================================================
[package]
name = "leaking"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/07_threads/03_leak/src/lib.rs
================================================
// TODO: Given a vector of integers, leak its heap allocation.
// Then split the resulting static slice into two halves and
// sum each half in a separate thread.
// Hint: check out `Vec::leak`.
use std::thread;
pub fn sum(v: Vec<i32>) -> i32 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
assert_eq!(sum(vec![]), 0);
}
#[test]
fn one() {
assert_eq!(sum(vec![1]), 1);
}
#[test]
fn five() {
assert_eq!(sum(vec![1, 2, 3, 4, 5]), 15);
}
#[test]
fn nine() {
assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]), 45);
}
#[test]
fn ten() {
assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 55);
}
}
================================================
FILE: exercises/07_threads/04_scoped_threads/Cargo.toml
================================================
[package]
name = "scoped_threads"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/07_threads/04_scoped_threads/src/lib.rs
================================================
// TODO: Given a vector of integers, split it in two halves
// and compute the sum of each half in a separate thread.
// Don't perform any heap allocation. Don't leak any memory.
pub fn sum(v: Vec<i32>) -> i32 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
assert_eq!(sum(vec![]), 0);
}
#[test]
fn one() {
assert_eq!(sum(vec![1]), 1);
}
#[test]
fn five() {
assert_eq!(sum(vec![1, 2, 3, 4, 5]), 15);
}
#[test]
fn nine() {
assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]), 45);
}
#[test]
fn ten() {
assert_eq!(sum(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 55);
}
}
================================================
FILE: exercises/07_threads/05_channels/Cargo.toml
================================================
[package]
name = "channels"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/07_threads/05_channels/src/data.rs
================================================
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
================================================
FILE: exercises/07_threads/05_channels/src/lib.rs
================================================
use std::sync::mpsc::{Receiver, Sender};
pub mod data;
pub mod store;
pub enum Command {
Insert(todo!()),
}
// Start the system by spawning the server thread.
// It returns a `Sender` instance which can then be used
// by one or more clients to interact with the server.
pub fn launch() -> Sender<Command> {
let (sender, receiver) = std::sync::mpsc::channel();
std::thread::spawn(move || server(receiver));
sender
}
// TODO: The server task should **never** stop.
// Enter a loop: wait for a command to show up in
// the channel, then execute it, then start waiting
// for the next command.
pub fn server(receiver: Receiver<Command>) {}
================================================
FILE: exercises/07_threads/05_channels/src/store.rs
================================================
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Ticket>,
counter: u64,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: BTreeMap::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
self.tickets.insert(id, ticket);
id
}
}
================================================
FILE: exercises/07_threads/05_channels/tests/insert.rs
================================================
// TODO: Set `move_forward` to `true` in `ready` when you think you're done with this exercise.
// Feel free to call an instructor to verify your solution!
use channels::data::TicketDraft;
use channels::{launch, Command};
use std::time::Duration;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn a_thread_is_spawned() {
let sender = launch();
std::thread::sleep(Duration::from_millis(200));
sender
.send(Command::Insert(TicketDraft {
title: ticket_title(),
description: ticket_description(),
}))
// If the thread is no longer running, this will panic
// because the channel will be closed.
.expect("Did you actually spawn a thread? The channel is closed!");
}
#[test]
fn ready() {
// There's very little that we can check automatically in this exercise,
// since our server doesn't expose any **read** actions.
// We have no way to know if the inserts are actually happening and if they
// are happening correctly.
let move_forward = false;
assert!(move_forward);
}
================================================
FILE: exercises/07_threads/06_interior_mutability/Cargo.toml
================================================
[package]
name = "interior_mutability"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/07_threads/06_interior_mutability/src/lib.rs
================================================
// TODO: Use `Rc` and `RefCell` to implement `DropTracker<T>`, a wrapper around a value of type `T`
// that increments a shared `usize` counter every time the wrapped value is dropped.
use std::cell::RefCell;
use std::rc::Rc;
pub struct DropTracker<T> {
value: T,
counter: todo!(),
}
impl<T> DropTracker<T> {
pub fn new(value: T, counter: todo!()) -> Self {
Self { value, counter }
}
}
impl<T> Drop for DropTracker<T> {
fn drop(&mut self) {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let counter = Rc::new(RefCell::new(0));
let _ = DropTracker::new((), Rc::clone(&counter));
assert_eq!(*counter.borrow(), 1);
}
#[test]
fn multiple() {
let counter = Rc::new(RefCell::new(0));
{
let a = DropTracker::new(5, Rc::clone(&counter));
let b = DropTracker::new(6, Rc::clone(&counter));
}
assert_eq!(*counter.borrow(), 2);
}
}
================================================
FILE: exercises/07_threads/07_ack/Cargo.toml
================================================
[package]
name = "response"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/07_threads/07_ack/src/data.rs
================================================
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
================================================
FILE: exercises/07_threads/07_ack/src/lib.rs
================================================
use std::sync::mpsc::{Receiver, Sender};
use crate::store::TicketStore;
pub mod data;
pub mod store;
// Refer to the tests to understand the expected schema.
pub enum Command {
Insert { todo!() },
Get { todo!() }
}
pub fn launch() -> Sender<Command> {
let (sender, receiver) = std::sync::mpsc::channel();
std::thread::spawn(move || server(receiver));
sender
}
// TODO: handle incoming commands as expected.
pub fn server(receiver: Receiver<Command>) {
let mut store = TicketStore::new();
loop {
match receiver.recv() {
Ok(Command::Insert {}) => {
todo!()
}
Ok(Command::Get {
todo!()
}) => {
todo!()
}
Err(_) => {
// There are no more senders, so we can safely break
// and shut down the server.
break
},
}
}
}
================================================
FILE: exercises/07_threads/07_ack/src/store.rs
================================================
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Ticket>,
counter: u64,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: BTreeMap::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
self.tickets.insert(id, ticket);
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
self.tickets.get(&id)
}
}
================================================
FILE: exercises/07_threads/07_ack/tests/insert.rs
================================================
use response::data::{Status, Ticket, TicketDraft};
use response::store::TicketId;
use response::{launch, Command};
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn insert_works() {
let sender = launch();
let (response_sender, response_receiver) = std::sync::mpsc::channel();
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let command = Command::Insert {
draft: draft.clone(),
response_sender,
};
sender
.send(command)
// If the thread is no longer running, this will panic
// because the channel will be closed.
.expect("Did you actually spawn a thread? The channel is closed!");
let ticket_id: TicketId = response_receiver.recv().expect("No response received!");
let (response_sender, response_receiver) = std::sync::mpsc::channel();
let command = Command::Get {
id: ticket_id,
response_sender,
};
sender
.send(command)
.expect("Did you actually spawn a thread? The channel is closed!");
let ticket: Ticket = response_receiver
.recv()
.expect("No response received!")
.unwrap();
assert_eq!(ticket_id, ticket.id);
assert_eq!(ticket.status, Status::ToDo);
assert_eq!(ticket.title, draft.title);
assert_eq!(ticket.description, draft.description);
}
================================================
FILE: exercises/07_threads/08_client/Cargo.toml
================================================
[package]
name = "client"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/07_threads/08_client/src/data.rs
================================================
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
================================================
FILE: exercises/07_threads/08_client/src/lib.rs
================================================
use crate::data::{Ticket, TicketDraft};
use crate::store::{TicketId, TicketStore};
use std::sync::mpsc::{Receiver, Sender};
pub mod data;
pub mod store;
#[derive(Clone)]
// TODO: flesh out the client implementation.
pub struct TicketStoreClient {}
impl TicketStoreClient {
// Feel free to panic on all errors, for simplicity.
pub fn insert(&self, draft: TicketDraft) -> TicketId {
todo!()
}
pub fn get(&self, id: TicketId) -> Option<Ticket> {
todo!()
}
}
pub fn launch() -> TicketStoreClient {
let (sender, receiver) = std::sync::mpsc::channel();
std::thread::spawn(move || server(receiver));
todo!()
}
// No longer public! This becomes an internal detail of the library now.
enum Command {
Insert {
draft: TicketDraft,
response_channel: Sender<TicketId>,
},
Get {
id: TicketId,
response_channel: Sender<Option<Ticket>>,
},
}
fn server(receiver: Receiver<Command>) {
let mut store = TicketStore::new();
loop {
match receiver.recv() {
Ok(Command::Insert {
draft,
response_channel,
}) => {
let id = store.add_ticket(draft);
let _ = response_channel.send(id);
}
Ok(Command::Get {
id,
response_channel,
}) => {
let ticket = store.get(id);
let _ = response_channel.send(ticket.cloned());
}
Err(_) => {
// There are no more senders, so we can safely break
// and shut down the server.
break;
}
}
}
}
================================================
FILE: exercises/07_threads/08_client/src/store.rs
================================================
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Ticket>,
counter: u64,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: BTreeMap::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
self.tickets.insert(id, ticket);
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
self.tickets.get(&id)
}
}
================================================
FILE: exercises/07_threads/08_client/tests/insert.rs
================================================
use client::data::{Status, TicketDraft};
use client::launch;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn insert_works() {
// Notice how much simpler the test is now that we have a client to handle the details!
let client = launch();
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let ticket_id = client.insert(draft.clone());
let client2 = client.clone();
let ticket = client2.get(ticket_id).unwrap();
assert_eq!(ticket_id, ticket.id);
assert_eq!(ticket.status, Status::ToDo);
assert_eq!(ticket.title, draft.title);
assert_eq!(ticket.description, draft.description);
}
================================================
FILE: exercises/07_threads/09_bounded/Cargo.toml
================================================
[package]
name = "bounded"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/07_threads/09_bounded/src/data.rs
================================================
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
================================================
FILE: exercises/07_threads/09_bounded/src/lib.rs
================================================
// TODO: Convert the implementation to use bounded channels.
use crate::data::{Ticket, TicketDraft};
use crate::store::{TicketId, TicketStore};
use std::sync::mpsc::{Receiver, Sender};
pub mod data;
pub mod store;
#[derive(Clone)]
pub struct TicketStoreClient {
sender: todo!(),
}
impl TicketStoreClient {
pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, todo!()> {
todo!()
}
pub fn get(&self, id: TicketId) -> Result<Option<Ticket>, todo!()> {
todo!()
}
}
pub fn launch(capacity: usize) -> TicketStoreClient {
todo!();
std::thread::spawn(move || server(receiver));
todo!()
}
enum Command {
Insert {
draft: TicketDraft,
response_channel: todo!(),
},
Get {
id: TicketId,
response_channel: todo!(),
},
}
pub fn server(receiver: Receiver<Command>) {
let mut store = TicketStore::new();
loop {
match receiver.recv() {
Ok(Command::Insert {
draft,
response_channel,
}) => {
let id = store.add_ticket(draft);
todo!()
}
Ok(Command::Get {
id,
response_channel,
}) => {
let ticket = store.get(id);
todo!()
}
Err(_) => {
// There are no more senders, so we can safely break
// and shut down the server.
break;
}
}
}
}
================================================
FILE: exercises/07_threads/09_bounded/src/store.rs
================================================
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Ticket>,
counter: u64,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: BTreeMap::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
self.tickets.insert(id, ticket);
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
self.tickets.get(&id)
}
}
================================================
FILE: exercises/07_threads/09_bounded/tests/insert.rs
================================================
use bounded::data::{Status, TicketDraft};
use bounded::launch;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let client = launch(5);
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let ticket_id = client.insert(draft.clone()).unwrap();
let client2 = client.clone();
let ticket = client2.get(ticket_id).unwrap().unwrap();
assert_eq!(ticket_id, ticket.id);
assert_eq!(ticket.status, Status::ToDo);
assert_eq!(ticket.title, draft.title);
assert_eq!(ticket.description, draft.description);
}
================================================
FILE: exercises/07_threads/10_patch/Cargo.toml
================================================
[package]
name = "patch"
version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.69"
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/07_threads/10_patch/src/data.rs
================================================
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketPatch {
pub id: TicketId,
pub title: Option<TicketTitle>,
pub description: Option<TicketDescription>,
pub status: Option<Status>,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
================================================
FILE: exercises/07_threads/10_patch/src/lib.rs
================================================
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
// TODO: Implement the patching functionality.
use crate::data::{Ticket, TicketDraft, TicketPatch};
use crate::store::{TicketId, TicketStore};
pub mod data;
pub mod store;
#[derive(Clone)]
pub struct TicketStoreClient {
sender: SyncSender<Command>,
}
impl TicketStoreClient {
pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, OverloadedError> {
let (response_sender, response_receiver) = sync_channel(1);
self.sender
.try_send(Command::Insert {
draft,
response_channel: response_sender,
})
.map_err(|_| OverloadedError)?;
Ok(response_receiver.recv().unwrap())
}
pub fn get(&self, id: TicketId) -> Result<Option<Ticket>, OverloadedError> {
let (response_sender, response_receiver) = sync_channel(1);
self.sender
.try_send(Command::Get {
id,
response_channel: response_sender,
})
.map_err(|_| OverloadedError)?;
Ok(response_receiver.recv().unwrap())
}
pub fn update(&self, ticket_patch: TicketPatch) -> Result<(), OverloadedError> {}
}
#[derive(Debug, thiserror::Error)]
#[error("The store is overloaded")]
pub struct OverloadedError;
pub fn launch(capacity: usize) -> TicketStoreClient {
let (sender, receiver) = sync_channel(capacity);
std::thread::spawn(move || server(receiver));
TicketStoreClient { sender }
}
enum Command {
Insert {
draft: TicketDraft,
response_channel: SyncSender<TicketId>,
},
Get {
id: TicketId,
response_channel: SyncSender<Option<Ticket>>,
},
Update {
patch: TicketPatch,
response_channel: SyncSender<()>,
},
}
pub fn server(receiver: Receiver<Command>) {
let mut store = TicketStore::new();
loop {
match receiver.recv() {
Ok(Command::Insert {
draft,
response_channel,
}) => {
let id = store.add_ticket(draft);
let _ = response_channel.send(id);
}
Ok(Command::Get {
id,
response_channel,
}) => {
let ticket = store.get(id);
let _ = response_channel.send(ticket.cloned());
}
Ok(Command::Update {
patch,
response_channel,
}) => {
todo!()
}
Err(_) => {
// There are no more senders, so we can safely break
// and shut down the server.
break;
}
}
}
}
================================================
FILE: exercises/07_threads/10_patch/src/store.rs
================================================
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Ticket>,
counter: u64,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: BTreeMap::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
self.tickets.insert(id, ticket);
id
}
pub fn get(&self, id: TicketId) -> Option<&Ticket> {
self.tickets.get(&id)
}
pub fn get_mut(&mut self, id: TicketId) -> Option<&mut Ticket> {
self.tickets.get_mut(&id)
}
}
================================================
FILE: exercises/07_threads/10_patch/tests/check.rs
================================================
use patch::data::{Status, TicketDraft, TicketPatch};
use patch::launch;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let client = launch(5);
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let ticket_id = client.insert(draft.clone()).unwrap();
let ticket = client.get(ticket_id).unwrap().unwrap();
assert_eq!(ticket_id, ticket.id);
assert_eq!(ticket.status, Status::ToDo);
assert_eq!(ticket.title, draft.title);
assert_eq!(ticket.description, draft.description);
let patch = TicketPatch {
id: ticket_id,
title: None,
description: None,
status: Some(Status::InProgress),
};
client.update(patch).unwrap();
let ticket = client.get(ticket_id).unwrap().unwrap();
assert_eq!(ticket.id, ticket_id);
assert_eq!(ticket.status, Status::InProgress);
}
================================================
FILE: exercises/07_threads/11_locks/Cargo.toml
================================================
[package]
name = "locks"
version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.69"
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/07_threads/11_locks/src/data.rs
================================================
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
================================================
FILE: exercises/07_threads/11_locks/src/lib.rs
================================================
// TODO: Fill in the missing methods for `TicketStore`.
// Notice how we no longer need a separate update command: `Get` now returns a handle to the ticket
// which allows the caller to both modify and read the ticket.
use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError};
use std::sync::{Arc, Mutex};
use crate::data::{Ticket, TicketDraft};
use crate::store::{TicketId, TicketStore};
pub mod data;
pub mod store;
#[derive(Clone)]
pub struct TicketStoreClient {
sender: SyncSender<Command>,
}
impl TicketStoreClient {
pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, OverloadedError> {
let (response_sender, response_receiver) = sync_channel(1);
self.sender
.try_send(Command::Insert {
draft,
response_channel: response_sender,
})
.map_err(|_| OverloadedError)?;
Ok(response_receiver.recv().unwrap())
}
pub fn get(&self, id: TicketId) -> Result<Option<Arc<Mutex<Ticket>>>, OverloadedError> {
let (response_sender, response_receiver) = sync_channel(1);
self.sender
.try_send(Command::Get {
id,
response_channel: response_sender,
})
.map_err(|_| OverloadedError)?;
Ok(response_receiver.recv().unwrap())
}
}
#[derive(Debug, thiserror::Error)]
#[error("The store is overloaded")]
pub struct OverloadedError;
pub fn launch(capacity: usize) -> TicketStoreClient {
let (sender, receiver) = sync_channel(capacity);
std::thread::spawn(move || server(receiver));
TicketStoreClient { sender }
}
enum Command {
Insert {
draft: TicketDraft,
response_channel: SyncSender<TicketId>,
},
Get {
id: TicketId,
response_channel: SyncSender<Option<Arc<Mutex<Ticket>>>>,
},
}
pub fn server(receiver: Receiver<Command>) {
let mut store = TicketStore::new();
loop {
match receiver.recv() {
Ok(Command::Insert {
draft,
response_channel,
}) => {
let id = store.add_ticket(draft);
let _ = response_channel.send(id);
}
Ok(Command::Get {
id,
response_channel,
}) => {
let ticket = store.get(id);
let _ = response_channel.send(ticket);
}
Err(_) => {
// There are no more senders, so we can safely break
// and shut down the server.
break;
}
}
}
}
================================================
FILE: exercises/07_threads/11_locks/src/store.rs
================================================
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Arc<Mutex<Ticket>>>,
counter: u64,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: BTreeMap::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
todo!();
id
}
// The `get` method should return a handle to the ticket
// which allows the caller to either read or modify the ticket.
pub fn get(&self, id: TicketId) -> Option<todo!()> {
todo!()
}
}
================================================
FILE: exercises/07_threads/11_locks/tests/check.rs
================================================
use locks::data::{Status, TicketDraft};
use locks::launch;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let client = launch(5);
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let ticket_id = client.insert(draft.clone()).unwrap();
let ticket = client.get(ticket_id).unwrap().unwrap();
{
let mut ticket = ticket.lock().unwrap();
assert_eq!(ticket_id, ticket.id);
assert_eq!(ticket.status, Status::ToDo);
assert_eq!(ticket.title, draft.title);
assert_eq!(ticket.description, draft.description);
ticket.status = Status::InProgress;
}
let ticket = client.get(ticket_id).unwrap().unwrap();
{
let ticket = ticket.lock().unwrap();
assert_eq!(ticket_id, ticket.id);
assert_eq!(ticket.status, Status::InProgress);
}
}
================================================
FILE: exercises/07_threads/12_rw_lock/Cargo.toml
================================================
[package]
name = "rwlock"
version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.69"
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/07_threads/12_rw_lock/src/data.rs
================================================
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
================================================
FILE: exercises/07_threads/12_rw_lock/src/lib.rs
================================================
// TODO: Replace `Mutex` with `RwLock` in the `TicketStore` struct and
// all other relevant places to allow multiple readers to access the ticket store concurrently.
use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError};
use std::sync::{Arc, Mutex};
use crate::data::{Ticket, TicketDraft};
use crate::store::{TicketId, TicketStore};
pub mod data;
pub mod store;
#[derive(Clone)]
pub struct TicketStoreClient {
sender: SyncSender<Command>,
}
impl TicketStoreClient {
pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, OverloadedError> {
let (response_sender, response_receiver) = sync_channel(1);
self.sender
.try_send(Command::Insert {
draft,
response_channel: response_sender,
})
.map_err(|_| OverloadedError)?;
Ok(response_receiver.recv().unwrap())
}
pub fn get(&self, id: TicketId) -> Result<Option<Arc<Mutex<Ticket>>>, OverloadedError> {
let (response_sender, response_receiver) = sync_channel(1);
self.sender
.try_send(Command::Get {
id,
response_channel: response_sender,
})
.map_err(|_| OverloadedError)?;
Ok(response_receiver.recv().unwrap())
}
}
#[derive(Debug, thiserror::Error)]
#[error("The store is overloaded")]
pub struct OverloadedError;
pub fn launch(capacity: usize) -> TicketStoreClient {
let (sender, receiver) = sync_channel(capacity);
std::thread::spawn(move || server(receiver));
TicketStoreClient { sender }
}
enum Command {
Insert {
draft: TicketDraft,
response_channel: SyncSender<TicketId>,
},
Get {
id: TicketId,
response_channel: SyncSender<Option<Arc<Mutex<Ticket>>>>,
},
}
pub fn server(receiver: Receiver<Command>) {
let mut store = TicketStore::new();
loop {
match receiver.recv() {
Ok(Command::Insert {
draft,
response_channel,
}) => {
let id = store.add_ticket(draft);
let _ = response_channel.send(id);
}
Ok(Command::Get {
id,
response_channel,
}) => {
let ticket = store.get(id);
let _ = response_channel.send(ticket);
}
Err(_) => {
// There are no more senders, so we can safely break
// and shut down the server.
break;
}
}
}
}
================================================
FILE: exercises/07_threads/12_rw_lock/src/store.rs
================================================
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Arc<Mutex<Ticket>>>,
counter: u64,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: BTreeMap::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
let ticket = Arc::new(Mutex::new(ticket));
self.tickets.insert(id, ticket);
id
}
// The `get` method should return a handle to the ticket
// which allows the caller to either read or modify the ticket.
pub fn get(&self, id: TicketId) -> Option<Arc<Mutex<Ticket>>> {
self.tickets.get(&id).cloned()
}
}
================================================
FILE: exercises/07_threads/12_rw_lock/tests/check.rs
================================================
use rwlock::data::{Status, TicketDraft};
use rwlock::launch;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
#[test]
fn works() {
let client = launch(5);
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
let ticket_id = client.insert(draft.clone()).unwrap();
let ticket = client.get(ticket_id).unwrap().unwrap();
let lock1 = ticket.read().unwrap();
{
let ticket = ticket.read().unwrap();
assert_eq!(ticket_id, ticket.id);
assert_eq!(ticket.status, Status::ToDo);
assert_eq!(ticket.title, draft.title);
assert_eq!(ticket.description, draft.description);
}
drop(lock1);
let ticket = client.get(ticket_id).unwrap().unwrap();
{
let mut ticket = ticket.write().unwrap();
ticket.status = Status::InProgress;
}
}
================================================
FILE: exercises/07_threads/13_without_channels/Cargo.toml
================================================
[package]
name = "without_channels"
version = "0.1.0"
edition = "2021"
[dependencies]
ticket_fields = { path = "../../../helpers/ticket_fields" }
================================================
FILE: exercises/07_threads/13_without_channels/src/data.rs
================================================
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};
#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
pub id: TicketId,
pub title: TicketTitle,
pub description: TicketDescription,
pub status: Status,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
pub title: TicketTitle,
pub description: TicketDescription,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
ToDo,
InProgress,
Done,
}
================================================
FILE: exercises/07_threads/13_without_channels/src/lib.rs
================================================
// TODO: You don't actually have to change anything in the library itself!
// We mostly had to **remove** code (the client type, the launch function, the command enum)
// that's no longer necessary.
// Fix the `todo!()` in the testing code and see how the new design can be used.
pub mod data;
pub mod store;
================================================
FILE: exercises/07_threads/13_without_channels/src/store.rs
================================================
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use crate::data::{Status, Ticket, TicketDraft};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);
#[derive(Clone)]
pub struct TicketStore {
tickets: BTreeMap<TicketId, Arc<RwLock<Ticket>>>,
counter: u64,
}
impl TicketStore {
pub fn new() -> Self {
Self {
tickets: BTreeMap::new(),
counter: 0,
}
}
pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
let id = TicketId(self.counter);
self.counter += 1;
let ticket = Ticket {
id,
title: ticket.title,
description: ticket.description,
status: Status::ToDo,
};
let ticket = Arc::new(RwLock::new(ticket));
self.tickets.insert(id, ticket);
id
}
pub fn get(&self, id: TicketId) -> Option<Arc<RwLock<Ticket>>> {
self.tickets.get(&id).cloned()
}
}
================================================
FILE: exercises/07_threads/13_without_channels/tests/check.rs
================================================
use std::sync::{Arc, RwLock};
use std::thread::spawn;
use ticket_fields::test_helpers::{ticket_description, ticket_title};
use without_channels::data::TicketDraft;
use without_channels::store::TicketStore;
#[test]
fn works() {
let store = todo!();
let store1 = store.clone();
let client1 = spawn(move || {
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
store1.write().unwrap().add_ticket(draft)
});
let store2 = store.clone();
let client2 = spawn(move || {
let draft = TicketDraft {
title: ticket_title(),
description: ticket_description(),
};
store2.write().unwrap().add_ticket(draft)
});
let ticket_id1 = client1.join().unwrap();
let ticket_id2 = client2.join().unwrap();
let reader = store.read().unwrap();
let ticket1 = reader.get(ticket_id1).unwrap();
assert_eq!(ticket_id1, ticket1.read().unwrap().id);
let ticket2 = reader.get(ticket_id2).unwrap();
assert_eq!(ticket_id2, ticket2.read().unwrap().id);
}
================================================
FILE: exercises/07_threads/14_sync/Cargo.toml
================================================
[package]
name = "sync"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/07_threads/14_sync/src/lib.rs
================================================
// Not much to be exercised on `Sync`, just a thing to remember.
fn outro() -> &'static str {
"I have a good understanding of __!"
}
#[cfg(test)]
mod tests {
use crate::outro;
#[test]
fn test_outro() {
assert_eq!(outro(), "I have a good understanding of Send and Sync!");
}
}
================================================
FILE: exercises/08_futures/00_intro/Cargo.toml
================================================
[package]
name = "intro_08"
version = "0.1.0"
edition = "2021"
================================================
FILE: exercises/08_futures/00_intro/src/lib.rs
================================================
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to _!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to learn about futures!");
}
}
================================================
FILE: exercises/08_futures/01_async_fn/Cargo.toml
================================================
[package]
name = "async_fn"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.100"
tokio = { version = "1", features = ["full"] }
================================================
FILE: exercises/08_futures/01_async_fn/src/lib.rs
================================================
use tokio::net::TcpListener;
// TODO: write an echo server that accepts incoming TCP connections and
// echoes the received data back to the client.
// `echo` should not return when it finishes processing a connection, but should
// continue to accept new connections.
//
// Hint: you should rely on `tokio`'s structs and methods to implement the echo server.
// In particular:
// - `tokio::net::TcpListener::accept` to process the next incoming connection
// - `tokio::net::TcpStream::split` to obtain a reader and a writer from the socket
// - `tokio::io::copy` to copy data from the reader to the writer
pub async fn echo(listener: TcpListener) -> Result<(), anyhow::Error> {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::test]
async fn test_echo() {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(echo(listener));
let requests = vec!["hello", "world", "foo", "bar"];
for request in requests {
let mut socket = tokio::net::TcpStream::connect(addr).await.unwrap();
let (mut reader, mut writer) = socket.split();
// Send the request
writer.write_all(request.as_bytes()).await.unwrap();
// Close the write side of the socket
writer.shutdown().await.unwrap();
// Read the response
let mut buf = Vec::with_capacity(request.len());
reader.read_to_end(&mut buf).await.unwrap();
assert_eq!(&buf, request.as_bytes());
}
}
}
================================================
FILE: exercises/08_futures/02_spawn/Cargo.toml
================================================
[package]
name = "spawn"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.100"
tokio = { version = "1", features = ["full"] }
================================================
FILE: exercises/08_futures/02_spawn/src/lib.rs
================================================
use tokio::net::TcpListener;
// TODO: write an echo server that accepts TCP connections on two listeners, concurrently.
// Multiple connections (on the same listeners) should be processed concurrently.
// The received data should be echoed back to the client.
pub async fn echoes(first: TcpListener, second: TcpListener) -> Result<(), anyhow::Error> {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::SocketAddr;
use std::panic;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::task::JoinSet;
async fn bind_random() -> (TcpListener, SocketAddr) {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
(listener, addr)
}
#[tokio::test]
async fn test_echo() {
let (first_listener, first_addr) = bind_random().await;
let (second_listener, second_addr) = bind_random().await;
tokio::spawn(echoes(first_listener, second_listener));
let requests = vec!["hello", "world", "foo", "bar"];
let mut join_set = JoinSet::new();
for request in requests.clone() {
for addr in [first_addr, second_addr] {
join_set.spawn(async move {
let mut socket = tokio::net::TcpStream::connect(addr).await.unwrap();
let (mut reader, mut writer) = socket.split();
// Send the request
writer.write_all(request.as_bytes()).await.unwrap();
// Close the write side of the socket
writer.shutdown().await.unwrap();
// Read the response
let mut buf = Vec::with_capacity(request.len());
reader.read_to_end(&mut buf).await.unwrap();
assert_eq!(&buf, request.as_bytes());
});
}
}
while let Some(outcome) = join_set.join_next().await {
if let Err(e) = outcome {
if let Ok(reason) = e.try_into_panic() {
panic::resume_unwind(reason);
}
}
}
}
}
================================================
FILE: exercises/08_futures/03_runtime/Cargo.toml
================================================
[package]
name = "runtime"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.100"
tokio = { version = "1", features = ["full"] }
================================================
FILE: exercises/08_futures/03_runtime/src/lib.rs
================================================
// TODO: Implement the `fixed_reply` function. It should accept two `TcpListener` instances,
// accept connections on both of them concurrently, and always reply to clients by sending
// the `Display` representation of the `reply` argument as a response.
use std::fmt::Display;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpListener;
pub async fn fixed_reply<T>(first: TcpListener, second: TcpListener, reply: T)
where
// `T` cannot be cloned. How do you share it between the two server tasks?
T: Display + Send + Sync + 'static,
{
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::SocketAddr;
use std::panic;
use tokio::io::AsyncReadExt;
use tokio::task::JoinSet;
async fn bind_random() -> (TcpListener, SocketAddr) {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
(listener, addr)
}
#[tokio::test]
async fn test_echo() {
let (first_listener, first_addr) = bind_random().await;
let (second_listener, second_addr) = bind_random().await;
let reply = "Yo";
tokio::spawn(fixed_reply(first_listener, second_listener, reply));
let mut join_set = JoinSet::new();
for _ in 0..3 {
for addr in [first_addr, second_addr] {
join_set.spawn(async move {
let m
gitextract_716k_gfd/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .wr.toml
├── Cargo.toml
├── README.md
├── dprint.json
├── exercises/
│ ├── 01_intro/
│ │ ├── 00_welcome/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 01_syntax/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 02_basic_calculator/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_integers/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_variables/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_if_else/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_panics/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_factorial/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_while/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_for/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_overflow/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_saturating/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 10_as_casting/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 03_ticket_v1/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_struct/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_validation/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_modules/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_visibility/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_encapsulation/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_ownership/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_setters/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_stack/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_heap/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 10_references_in_memory/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 11_destructor/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 12_outro/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── tests/
│ │ └── integration.rs
│ ├── 04_traits/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_trait/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_orphan_rule/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_operator_overloading/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_derive/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_trait_bounds/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_str_slice/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_deref/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_sized/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_from/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 10_assoc_vs_generic/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 11_clone/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 12_copy/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 13_drop/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 14_outro/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── tests/
│ │ └── integration.rs
│ ├── 05_ticket_v2/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_enum/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_match/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_variants_with_data/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_if_let/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_nullability/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_fallibility/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_unwrap/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_error_enums/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_error_trait/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 10_packages/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── main.rs
│ │ ├── 11_dependencies/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 12_thiserror/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 13_try_from/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 14_source/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ ├── lib.rs
│ │ │ └── status.rs
│ │ └── 15_outro/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── description.rs
│ │ ├── lib.rs
│ │ ├── status.rs
│ │ └── title.rs
│ ├── 06_ticket_management/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_arrays/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_vec/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_resizing/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_iterators/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_iter/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 06_lifetimes/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_combinators/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 08_impl_trait/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 09_impl_trait_2/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 10_slices/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 11_mutable_slices/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 12_two_states/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 13_index/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 14_index_mut/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 15_hashmap/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── 16_btreemap/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 07_threads/
│ │ ├── 00_intro/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 01_threads/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 02_static/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 03_leak/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 04_scoped_threads/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 05_channels/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── insert.rs
│ │ ├── 06_interior_mutability/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── 07_ack/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── insert.rs
│ │ ├── 08_client/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── insert.rs
│ │ ├── 09_bounded/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── insert.rs
│ │ ├── 10_patch/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── check.rs
│ │ ├── 11_locks/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── check.rs
│ │ ├── 12_rw_lock/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── check.rs
│ │ ├── 13_without_channels/
│ │ │ ├── Cargo.toml
│ │ │ ├── src/
│ │ │ │ ├── data.rs
│ │ │ │ ├── lib.rs
│ │ │ │ └── store.rs
│ │ │ └── tests/
│ │ │ └── check.rs
│ │ └── 14_sync/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── 08_futures/
│ ├── 00_intro/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 01_async_fn/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 02_spawn/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 03_runtime/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 04_future/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 05_blocking/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 06_async_aware_primitives/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── 07_cancellation/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── 08_outro/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── helpers/
│ ├── common/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── json2redirects.sh
│ ├── mdbook-exercise-linker/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── mdbook-link-shortener/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── main.rs
│ └── ticket_fields/
│ ├── Cargo.toml
│ └── src/
│ ├── description.rs
│ ├── lib.rs
│ ├── test_helpers.rs
│ └── title.rs
└── site/
└── _redirects
SYMBOL INDEX (603 symbols across 131 files)
FILE: exercises/01_intro/00_welcome/src/lib.rs
function greeting (line 18) | fn greeting() -> &'static str {
function test_welcome (line 43) | fn test_welcome() {
FILE: exercises/01_intro/01_syntax/src/lib.rs
function compute (line 6) | fn compute(a, b) -> u32 {
function case (line 16) | fn case() {
FILE: exercises/02_basic_calculator/00_intro/src/lib.rs
function intro (line 1) | fn intro() -> &'static str {
function test_intro (line 11) | fn test_intro() {
FILE: exercises/02_basic_calculator/01_integers/src/lib.rs
function compute (line 1) | fn compute(a: u32, b: u32) -> u32 {
function case (line 12) | fn case() {
FILE: exercises/02_basic_calculator/02_variables/src/lib.rs
function speed (line 8) | pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
function case1 (line 21) | fn case1() {
function case2 (line 26) | fn case2() {
function case3 (line 31) | fn case3() {
FILE: exercises/02_basic_calculator/03_if_else/src/lib.rs
function magic_number (line 4) | fn magic_number(n: u32) -> u32 {
function one (line 13) | fn one() {
function two (line 18) | fn two() {
function six (line 23) | fn six() {
function nine (line 28) | fn nine() {
function high (line 33) | fn high() {
FILE: exercises/02_basic_calculator/04_panics/src/lib.rs
function speed (line 3) | fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
function case1 (line 14) | fn case1() {
function by_zero (line 23) | fn by_zero() {
FILE: exercises/02_basic_calculator/05_factorial/src/lib.rs
function first (line 18) | fn first() {
function second (line 23) | fn second() {
function third (line 28) | fn third() {
function fifth (line 33) | fn fifth() {
FILE: exercises/02_basic_calculator/06_while/src/lib.rs
function factorial (line 2) | pub fn factorial(n: u32) -> u32 {
function first (line 15) | fn first() {
function second (line 20) | fn second() {
function third (line 25) | fn third() {
function fifth (line 30) | fn fifth() {
FILE: exercises/02_basic_calculator/07_for/src/lib.rs
function factorial (line 2) | pub fn factorial(n: u32) -> u32 {
function first (line 11) | fn first() {
function second (line 16) | fn second() {
function third (line 21) | fn third() {
function fifth (line 26) | fn fifth() {
FILE: exercises/02_basic_calculator/08_overflow/src/lib.rs
function factorial (line 8) | pub fn factorial(n: u32) -> u32 {
function twentieth (line 21) | fn twentieth() {
function first (line 31) | fn first() {
function second (line 36) | fn second() {
function third (line 41) | fn third() {
function fifth (line 46) | fn fifth() {
FILE: exercises/02_basic_calculator/09_saturating/src/lib.rs
function factorial (line 1) | pub fn factorial(n: u32) -> u32 {
function twentieth (line 16) | fn twentieth() {
function first (line 21) | fn first() {
function second (line 26) | fn second() {
function third (line 31) | fn third() {
function fifth (line 36) | fn fifth() {
FILE: exercises/02_basic_calculator/10_as_casting/src/lib.rs
function u16_to_u32 (line 8) | fn u16_to_u32() {
function u8_to_i8 (line 14) | fn u8_to_i8() {
function bool_to_u8 (line 33) | fn bool_to_u8() {
FILE: exercises/03_ticket_v1/00_intro/src/lib.rs
function intro (line 1) | fn intro() -> &'static str {
function test_intro (line 11) | fn test_intro() {
FILE: exercises/03_ticket_v1/01_struct/src/lib.rs
function test_order_is_available (line 13) | fn test_order_is_available() {
function test_order_is_not_available (line 22) | fn test_order_is_not_available() {
FILE: exercises/03_ticket_v1/02_validation/src/lib.rs
type Ticket (line 1) | struct Ticket {
method new (line 20) | fn new(title: String, description: String, status: String) -> Self {
function title_cannot_be_empty (line 37) | fn title_cannot_be_empty() {
function description_cannot_be_empty (line 43) | fn description_cannot_be_empty() {
function title_cannot_be_longer_than_fifty_chars (line 49) | fn title_cannot_be_longer_than_fifty_chars() {
function description_cannot_be_longer_than_500_chars (line 55) | fn description_cannot_be_longer_than_500_chars() {
function status_must_be_valid (line 61) | fn status_must_be_valid() {
function done_is_allowed (line 66) | fn done_is_allowed() {
function in_progress_is_allowed (line 71) | fn in_progress_is_allowed() {
FILE: exercises/03_ticket_v1/03_modules/src/lib.rs
function create_todo_ticket (line 5) | fn create_todo_ticket(title: String, description: String) -> Ticket {
type Ticket (line 10) | struct Ticket {
method new (line 17) | fn new(title: String, description: String, status: String) -> Ticket {
FILE: exercises/03_ticket_v1/04_visibility/src/lib.rs
type Ticket (line 2) | struct Ticket {
method new (line 9) | fn new(title: String, description: String, status: String) -> Ticket {
function should_not_be_possible (line 46) | fn should_not_be_possible() {
function encapsulation_cannot_be_violated (line 61) | fn encapsulation_cannot_be_violated() {
FILE: exercises/03_ticket_v1/05_encapsulation/src/lib.rs
type Ticket (line 2) | pub struct Ticket {
method new (line 9) | pub fn new(title: String, description: String, status: String) -> Tick...
function description (line 45) | fn description() {
function title (line 51) | fn title() {
function status (line 57) | fn status() {
FILE: exercises/03_ticket_v1/06_ownership/src/lib.rs
type Ticket (line 6) | pub struct Ticket {
method new (line 13) | pub fn new(title: String, description: String, status: String) -> Tick...
method title (line 37) | pub fn title(self) -> String {
method description (line 41) | pub fn description(self) -> String {
method status (line 45) | pub fn status(self) -> String {
function works (line 55) | fn works() {
FILE: exercises/03_ticket_v1/07_setters/src/lib.rs
type Ticket (line 6) | pub struct Ticket {
method new (line 13) | pub fn new(title: String, description: String, status: String) -> Tick...
method title (line 37) | pub fn title(&self) -> &String {
method description (line 41) | pub fn description(&self) -> &String {
method status (line 45) | pub fn status(&self) -> &String {
function works (line 56) | fn works() {
function title_cannot_be_empty (line 69) | fn title_cannot_be_empty() {
function description_cannot_be_empty (line 75) | fn description_cannot_be_empty() {
function title_cannot_be_longer_than_fifty_chars (line 81) | fn title_cannot_be_longer_than_fifty_chars() {
function description_cannot_be_longer_than_500_chars (line 88) | fn description_cannot_be_longer_than_500_chars() {
function status_must_be_valid (line 95) | fn status_must_be_valid() {
FILE: exercises/03_ticket_v1/08_stack/src/lib.rs
function u16_size (line 8) | fn u16_size() {
function i32_size (line 13) | fn i32_size() {
function bool_size (line 18) | fn bool_size() {
FILE: exercises/03_ticket_v1/09_heap/src/lib.rs
type Ticket (line 1) | pub struct Ticket {
function string_size (line 15) | fn string_size() {
function ticket_size (line 20) | fn ticket_size() {
FILE: exercises/03_ticket_v1/10_references_in_memory/src/lib.rs
type Ticket (line 1) | pub struct Ticket {
function u16_ref_size (line 15) | fn u16_ref_size() {
function u64_mut_ref_size (line 20) | fn u64_mut_ref_size() {
function ticket_ref_size (line 25) | fn ticket_ref_size() {
FILE: exercises/03_ticket_v1/11_destructor/src/lib.rs
function outro (line 4) | fn outro() -> &'static str {
function test_outro (line 13) | fn test_outro() {
FILE: exercises/03_ticket_v1/12_outro/tests/integration.rs
function test_order (line 8) | fn test_order() {
function test_empty_product_name (line 29) | fn test_empty_product_name() {
function test_long_product_name (line 35) | fn test_long_product_name() {
function test_zero_quantity (line 41) | fn test_zero_quantity() {
function test_zero_unit_price (line 47) | fn test_zero_unit_price() {
FILE: exercises/04_traits/00_intro/src/lib.rs
function intro (line 1) | fn intro() -> &'static str {
function test_intro (line 11) | fn test_intro() {
FILE: exercises/04_traits/01_trait/src/lib.rs
function test_u32_is_even (line 11) | fn test_u32_is_even() {
function test_i32_is_even (line 17) | fn test_i32_is_even() {
FILE: exercises/04_traits/02_orphan_rule/src/lib.rs
method eq (line 8) | fn eq(&self, _other: &Self) -> bool {
FILE: exercises/04_traits/03_operator_overloading/src/lib.rs
type Ticket (line 3) | struct Ticket {
function test_partial_eq (line 18) | fn test_partial_eq() {
function test_description_not_matching (line 36) | fn test_description_not_matching() {
function test_title_not_matching (line 53) | fn test_title_not_matching() {
function test_status_not_matching (line 70) | fn test_status_not_matching() {
FILE: exercises/04_traits/04_derive/src/lib.rs
type Ticket (line 12) | struct Ticket {
function test_partial_eq (line 23) | fn test_partial_eq() {
function test_description_not_matching (line 41) | fn test_description_not_matching() {
function test_title_not_matching (line 58) | fn test_title_not_matching() {
function test_status_not_matching (line 75) | fn test_status_not_matching() {
FILE: exercises/04_traits/05_trait_bounds/src/lib.rs
function min (line 9) | pub fn min<T>(left: T, right: T) -> T {
FILE: exercises/04_traits/06_str_slice/src/lib.rs
type Ticket (line 3) | pub struct Ticket {
method new (line 10) | pub fn new(title: String, description: String, status: String) -> Tick...
method title (line 34) | pub fn title(&self) -> &String {
method description (line 38) | pub fn description(&self) -> &String {
method status (line 42) | pub fn status(&self) -> &String {
function test_type (line 54) | fn test_type() {
FILE: exercises/04_traits/07_deref/src/lib.rs
type Ticket (line 7) | pub struct Ticket {
method title (line 14) | pub fn title(&self) -> &str {
method description (line 18) | pub fn description(&self) -> &str {
function test_normalization (line 28) | fn test_normalization() {
FILE: exercises/04_traits/08_sized/src/lib.rs
function example (line 1) | pub fn example() {
FILE: exercises/04_traits/09_from/src/lib.rs
type WrappingU32 (line 3) | pub struct WrappingU32 {
function example (line 7) | fn example() {
FILE: exercises/04_traits/10_assoc_vs_generic/src/lib.rs
function test_power_u16 (line 21) | fn test_power_u16() {
function test_power_u32 (line 27) | fn test_power_u32() {
function test_power_ref_u32 (line 33) | fn test_power_ref_u32() {
FILE: exercises/04_traits/11_clone/src/lib.rs
function summary (line 4) | pub fn summary(ticket: Ticket) -> (Ticket, Summary) {
type Ticket (line 8) | pub struct Ticket {
method summary (line 15) | pub fn summary(self) -> Summary {
type Summary (line 23) | pub struct Summary {
FILE: exercises/04_traits/12_copy/src/lib.rs
type WrappingU32 (line 4) | pub struct WrappingU32 {
method new (line 9) | pub fn new(value: u32) -> Self {
function test_ops (line 19) | fn test_ops() {
FILE: exercises/04_traits/13_drop/src/lib.rs
function test_drop_bomb (line 11) | fn test_drop_bomb() {
function test_defused_drop_bomb (line 17) | fn test_defused_drop_bomb() {
FILE: exercises/04_traits/14_outro/tests/integration.rs
function test_saturating_u16 (line 4) | fn test_saturating_u16() {
FILE: exercises/05_ticket_v2/00_intro/src/lib.rs
function intro (line 1) | fn intro() -> &'static str {
function test_intro (line 11) | fn test_intro() {
FILE: exercises/05_ticket_v2/01_enum/src/lib.rs
type Ticket (line 7) | struct Ticket {
method new (line 18) | pub fn new(title: String, description: String, status: String) -> Tick...
method title (line 42) | pub fn title(&self) -> &String {
method description (line 46) | pub fn description(&self) -> &String {
method status (line 50) | pub fn status(&self) -> &String {
type Status (line 13) | enum Status {
function test_partial_eq (line 61) | fn test_partial_eq() {
function test_description_not_matching (line 78) | fn test_description_not_matching() {
function test_title_not_matching (line 95) | fn test_title_not_matching() {
function test_status_not_matching (line 112) | fn test_status_not_matching() {
FILE: exercises/05_ticket_v2/02_match/src/lib.rs
type Shape (line 1) | enum Shape {
method n_sides (line 11) | pub fn n_sides(&self) -> u8 {
function test_circle (line 21) | fn test_circle() {
function test_square (line 26) | fn test_square() {
function test_rectangle (line 31) | fn test_rectangle() {
function test_triangle (line 36) | fn test_triangle() {
function test_pentagon (line 41) | fn test_pentagon() {
FILE: exercises/05_ticket_v2/03_variants_with_data/src/lib.rs
type Ticket (line 6) | struct Ticket {
method new (line 20) | pub fn new(title: String, description: String, status: Status) -> Tick...
method assigned_to (line 40) | pub fn assigned_to(&self) -> &str {
type Status (line 13) | enum Status {
function test_todo (line 52) | fn test_todo() {
function test_done (line 59) | fn test_done() {
function test_in_progress (line 65) | fn test_in_progress() {
FILE: exercises/05_ticket_v2/04_if_let/src/lib.rs
type Shape (line 1) | enum Shape {
method radius (line 10) | pub fn radius(&self) -> f64 {
function test_circle (line 20) | fn test_circle() {
function test_square (line 26) | fn test_square() {
function test_rectangle (line 32) | fn test_rectangle() {
FILE: exercises/05_ticket_v2/05_nullability/src/lib.rs
type Ticket (line 4) | struct Ticket {
method new (line 18) | pub fn new(title: String, description: String, status: Status) -> Tick...
method assigned_to (line 38) | pub fn assigned_to(&self) -> Option<&String> {
type Status (line 11) | enum Status {
function test_todo (line 49) | fn test_todo() {
function test_done (line 55) | fn test_done() {
function test_in_progress (line 61) | fn test_in_progress() {
FILE: exercises/05_ticket_v2/06_fallibility/src/lib.rs
type Ticket (line 5) | struct Ticket {
method new (line 19) | pub fn new(title: String, description: String, status: Status) -> Tick...
type Status (line 12) | enum Status {
function title_cannot_be_empty (line 47) | fn title_cannot_be_empty() {
function description_cannot_be_empty (line 53) | fn description_cannot_be_empty() {
function title_cannot_be_longer_than_fifty_chars (line 59) | fn title_cannot_be_longer_than_fifty_chars() {
function description_cannot_be_longer_than_500_chars (line 66) | fn description_cannot_be_longer_than_500_chars() {
FILE: exercises/05_ticket_v2/07_unwrap/src/lib.rs
function easy_ticket (line 4) | fn easy_ticket(title: String, description: String, status: Status) -> Ti...
type Ticket (line 9) | struct Ticket {
method new (line 23) | pub fn new(title: String, description: String, status: Status) -> Resu...
type Status (line 16) | enum Status {
function title_cannot_be_empty (line 52) | fn title_cannot_be_empty() {
function template_description_is_used_if_empty (line 57) | fn template_description_is_used_if_empty() {
function title_cannot_be_longer_than_fifty_chars (line 64) | fn title_cannot_be_longer_than_fifty_chars() {
function template_description_is_used_if_too_long (line 69) | fn template_description_is_used_if_too_long() {
FILE: exercises/05_ticket_v2/08_error_enums/src/lib.rs
type TicketNewError (line 4) | enum TicketNewError {}
function easy_ticket (line 10) | fn easy_ticket(title: String, description: String, status: Status) -> Ti...
type Ticket (line 15) | struct Ticket {
method new (line 29) | pub fn new(
type Status (line 22) | enum Status {
function title_cannot_be_empty (line 62) | fn title_cannot_be_empty() {
function template_description_is_used_if_empty (line 67) | fn template_description_is_used_if_empty() {
function title_cannot_be_longer_than_fifty_chars (line 74) | fn title_cannot_be_longer_than_fifty_chars() {
function template_description_is_used_if_too_long (line 79) | fn template_description_is_used_if_too_long() {
FILE: exercises/05_ticket_v2/09_error_trait/src/lib.rs
type TicketNewError (line 6) | enum TicketNewError {
function easy_ticket (line 15) | fn easy_ticket(title: String, description: String, status: Status) -> Ti...
type Ticket (line 20) | struct Ticket {
method new (line 34) | pub fn new(
type Status (line 27) | enum Status {
function title_cannot_be_empty (line 76) | fn title_cannot_be_empty() {
function template_description_is_used_if_empty (line 81) | fn template_description_is_used_if_empty() {
function title_cannot_be_longer_than_fifty_chars (line 88) | fn title_cannot_be_longer_than_fifty_chars() {
function template_description_is_used_if_too_long (line 93) | fn template_description_is_used_if_too_long() {
function display_is_correctly_implemented (line 99) | fn display_is_correctly_implemented() {
FILE: exercises/05_ticket_v2/10_packages/src/main.rs
function main (line 9) | fn main() {
FILE: exercises/05_ticket_v2/12_thiserror/src/lib.rs
type TicketNewError (line 6) | enum TicketNewError {
type Ticket (line 14) | struct Ticket {
method new (line 28) | pub fn new(
type Status (line 21) | enum Status {
function title_cannot_be_empty (line 60) | fn title_cannot_be_empty() {
function description_cannot_be_empty (line 66) | fn description_cannot_be_empty() {
function title_cannot_be_longer_than_fifty_chars (line 72) | fn title_cannot_be_longer_than_fifty_chars() {
function description_cannot_be_too_long (line 78) | fn description_cannot_be_too_long() {
FILE: exercises/05_ticket_v2/13_try_from/src/lib.rs
type Status (line 5) | enum Status {
function test_try_from_string (line 17) | fn test_try_from_string() {
function test_try_from_str (line 29) | fn test_try_from_str() {
FILE: exercises/05_ticket_v2/14_source/src/lib.rs
type TicketNewError (line 17) | pub enum TicketNewError {
type Ticket (line 29) | pub struct Ticket {
method new (line 36) | pub fn new(title: String, description: String, status: String) -> Resu...
function invalid_status (line 68) | fn invalid_status() {
FILE: exercises/05_ticket_v2/14_source/src/status.rs
type Status (line 2) | pub enum Status {
type Error (line 9) | type Error = ParseStatusError;
method try_from (line 11) | fn try_from(value: String) -> Result<Self, Self::Error> {
type ParseStatusError (line 26) | pub struct ParseStatusError {
function test_try_from_string (line 36) | fn test_try_from_string() {
FILE: exercises/05_ticket_v2/15_outro/src/description.rs
type TicketDescription (line 5) | pub struct TicketDescription(String);
function test_try_from_string (line 13) | fn test_try_from_string() {
function test_try_from_empty_string (line 19) | fn test_try_from_empty_string() {
function test_try_from_long_string (line 25) | fn test_try_from_long_string() {
function test_try_from_str (line 35) | fn test_try_from_str() {
FILE: exercises/05_ticket_v2/15_outro/src/lib.rs
type Ticket (line 24) | pub struct Ticket {
FILE: exercises/05_ticket_v2/15_outro/src/status.rs
type Status (line 4) | pub enum Status {
function test_try_from_string (line 16) | fn test_try_from_string() {
function test_try_from_str (line 28) | fn test_try_from_str() {
function test_try_from_invalid (line 40) | fn test_try_from_invalid() {
FILE: exercises/05_ticket_v2/15_outro/src/title.rs
type TicketTitle (line 5) | pub struct TicketTitle(String);
function test_try_from_string (line 13) | fn test_try_from_string() {
function test_try_from_empty_string (line 19) | fn test_try_from_empty_string() {
function test_try_from_long_string (line 25) | fn test_try_from_long_string() {
function test_try_from_str (line 34) | fn test_try_from_str() {
FILE: exercises/06_ticket_management/00_intro/src/lib.rs
function intro (line 1) | fn intro() -> &'static str {
function test_intro (line 11) | fn test_intro() {
FILE: exercises/06_ticket_management/01_arrays/src/lib.rs
type WeekTemperatures (line 3) | pub struct WeekTemperatures {
method new (line 18) | pub fn new() -> Self {
method get_temperature (line 22) | pub fn get_temperature(&self, day: Weekday) -> Option<i32> {
method set_temperature (line 26) | pub fn set_temperature(&mut self, day: Weekday, temperature: i32) {
type Weekday (line 7) | pub enum Weekday {
function test_get_temperature (line 36) | fn test_get_temperature() {
FILE: exercises/06_ticket_management/02_vec/src/lib.rs
function fibonacci (line 13) | pub fn fibonacci(n: u32) -> u32 {
function first (line 26) | fn first() {
function second (line 31) | fn second() {
function third (line 36) | fn third() {
function tenth (line 41) | fn tenth() {
function thirtieth (line 46) | fn thirtieth() {
FILE: exercises/06_ticket_management/03_resizing/src/lib.rs
function resizing (line 4) | fn resizing() {
FILE: exercises/06_ticket_management/04_iterators/src/lib.rs
type TicketStore (line 12) | pub struct TicketStore {
method new (line 31) | pub fn new() -> Self {
method add_ticket (line 37) | pub fn add_ticket(&mut self, ticket: Ticket) {
type Ticket (line 17) | pub struct Ticket {
type Status (line 24) | pub enum Status {
function add_ticket (line 48) | fn add_ticket() {
FILE: exercises/06_ticket_management/05_iter/src/lib.rs
type TicketStore (line 9) | pub struct TicketStore {
method new (line 28) | pub fn new() -> Self {
method add_ticket (line 34) | pub fn add_ticket(&mut self, ticket: Ticket) {
type Ticket (line 14) | pub struct Ticket {
type Status (line 21) | pub enum Status {
function add_ticket (line 45) | fn add_ticket() {
FILE: exercises/06_ticket_management/06_lifetimes/src/lib.rs
type TicketStore (line 5) | pub struct TicketStore {
method new (line 24) | pub fn new() -> Self {
method add_ticket (line 30) | pub fn add_ticket(&mut self, ticket: Ticket) {
method iter (line 34) | pub fn iter(&self) -> std::slice::Iter<Ticket> {
type Ticket (line 10) | pub struct Ticket {
type Status (line 17) | pub enum Status {
function add_ticket (line 45) | fn add_ticket() {
FILE: exercises/06_ticket_management/07_combinators/src/lib.rs
type TicketStore (line 6) | pub struct TicketStore {
method new (line 25) | pub fn new() -> Self {
method add_ticket (line 31) | pub fn add_ticket(&mut self, ticket: Ticket) {
type Ticket (line 11) | pub struct Ticket {
type Status (line 18) | pub enum Status {
function todos (line 42) | fn todos() {
FILE: exercises/06_ticket_management/08_impl_trait/src/lib.rs
type TicketStore (line 6) | pub struct TicketStore {
method new (line 25) | pub fn new() -> Self {
method add_ticket (line 31) | pub fn add_ticket(&mut self, ticket: Ticket) {
type Ticket (line 11) | pub struct Ticket {
type Status (line 18) | pub enum Status {
function in_progress (line 42) | fn in_progress() {
FILE: exercises/06_ticket_management/09_impl_trait_2/src/lib.rs
type TicketStore (line 7) | pub struct TicketStore {
method new (line 26) | pub fn new() -> Self {
method add_ticket (line 36) | pub fn add_ticket(&mut self, ticket: impl Into<Ticket>) {
type Ticket (line 12) | pub struct Ticket {
method from (line 52) | fn from(draft: TicketDraft) -> Self {
type Status (line 19) | pub enum Status {
type TicketDraft (line 46) | struct TicketDraft {
function generic_add (line 62) | fn generic_add() {
FILE: exercises/06_ticket_management/10_slices/src/lib.rs
function empty (line 9) | fn empty() {
function one_element (line 15) | fn one_element() {
function multiple_elements (line 21) | fn multiple_elements() {
function array_slice (line 27) | fn array_slice() {
FILE: exercises/06_ticket_management/11_mutable_slices/src/lib.rs
function empty (line 9) | fn empty() {
function one (line 16) | fn one() {
function multiple (line 23) | fn multiple() {
FILE: exercises/06_ticket_management/12_two_states/src/lib.rs
type TicketStore (line 12) | pub struct TicketStore {
method new (line 41) | pub fn new() -> Self {
method add_ticket (line 47) | pub fn add_ticket(&mut self, ticket: Ticket) {
type TicketId (line 17) | pub struct TicketId(u64);
type Ticket (line 20) | pub struct Ticket {
type TicketDraft (line 28) | pub struct TicketDraft {
type Status (line 34) | pub enum Status {
function works (line 58) | fn works() {
FILE: exercises/06_ticket_management/13_index/src/lib.rs
type TicketStore (line 6) | pub struct TicketStore {
method new (line 36) | pub fn new() -> Self {
method add_ticket (line 43) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 56) | pub fn get(&self, id: TicketId) -> Option<&Ticket> {
type TicketId (line 12) | pub struct TicketId(u64);
type Ticket (line 15) | pub struct Ticket {
type TicketDraft (line 23) | pub struct TicketDraft {
type Status (line 29) | pub enum Status {
function works (line 67) | fn works() {
FILE: exercises/06_ticket_management/14_index_mut/src/lib.rs
type TicketStore (line 7) | pub struct TicketStore {
method new (line 37) | pub fn new() -> Self {
method add_ticket (line 44) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 57) | pub fn get(&self, id: TicketId) -> Option<&Ticket> {
type Output (line 63) | type Output = Ticket;
method index (line 65) | fn index(&self, index: TicketId) -> &Self::Output {
type Output (line 71) | type Output = Ticket;
method index (line 73) | fn index(&self, index: &TicketId) -> &Self::Output {
type TicketId (line 13) | pub struct TicketId(u64);
type Ticket (line 16) | pub struct Ticket {
type TicketDraft (line 24) | pub struct TicketDraft {
type Status (line 30) | pub enum Status {
function works (line 84) | fn works() {
FILE: exercises/06_ticket_management/15_hashmap/src/lib.rs
type TicketStore (line 9) | pub struct TicketStore {
method new (line 39) | pub fn new() -> Self {
method add_ticket (line 46) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 59) | pub fn get(&self, id: TicketId) -> Option<&Ticket> {
method get_mut (line 63) | pub fn get_mut(&mut self, id: TicketId) -> Option<&mut Ticket> {
type Output (line 69) | type Output = Ticket;
method index (line 71) | fn index(&self, index: TicketId) -> &Self::Output {
type Output (line 77) | type Output = Ticket;
method index (line 79) | fn index(&self, index: &TicketId) -> &Self::Output {
method index_mut (line 85) | fn index_mut(&mut self, index: TicketId) -> &mut Self::Output {
method index_mut (line 91) | fn index_mut(&mut self, index: &TicketId) -> &mut Self::Output {
type TicketId (line 15) | pub struct TicketId(u64);
type Ticket (line 18) | pub struct Ticket {
type TicketDraft (line 26) | pub struct TicketDraft {
type Status (line 32) | pub enum Status {
function works (line 102) | fn works() {
FILE: exercises/06_ticket_management/16_btreemap/src/lib.rs
type TicketStore (line 11) | pub struct TicketStore {
method new (line 41) | pub fn new() -> Self {
method add_ticket (line 48) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 61) | pub fn get(&self, id: TicketId) -> Option<&Ticket> {
method get_mut (line 65) | pub fn get_mut(&mut self, id: TicketId) -> Option<&mut Ticket> {
type Output (line 71) | type Output = Ticket;
method index (line 73) | fn index(&self, index: TicketId) -> &Self::Output {
type Output (line 79) | type Output = Ticket;
method index (line 81) | fn index(&self, index: &TicketId) -> &Self::Output {
method index_mut (line 87) | fn index_mut(&mut self, index: TicketId) -> &mut Self::Output {
method index_mut (line 93) | fn index_mut(&mut self, index: &TicketId) -> &mut Self::Output {
type TicketId (line 17) | pub struct TicketId(u64);
type Ticket (line 20) | pub struct Ticket {
type TicketDraft (line 28) | pub struct TicketDraft {
type Status (line 34) | pub enum Status {
function works (line 104) | fn works() {
FILE: exercises/07_threads/00_intro/src/lib.rs
function intro (line 1) | fn intro() -> &'static str {
function test_intro (line 11) | fn test_intro() {
FILE: exercises/07_threads/01_threads/src/lib.rs
function sum (line 17) | pub fn sum(v: Vec<i32>) -> i32 {
function empty (line 26) | fn empty() {
function one (line 31) | fn one() {
function five (line 36) | fn five() {
function nine (line 41) | fn nine() {
function ten (line 46) | fn ten() {
FILE: exercises/07_threads/02_static/src/lib.rs
function sum (line 6) | pub fn sum(slice: &'static [i32]) -> i32 {
function empty (line 15) | fn empty() {
function one (line 21) | fn one() {
function five (line 27) | fn five() {
function nine (line 33) | fn nine() {
function ten (line 39) | fn ten() {
FILE: exercises/07_threads/03_leak/src/lib.rs
function sum (line 8) | pub fn sum(v: Vec<i32>) -> i32 {
function empty (line 17) | fn empty() {
function one (line 22) | fn one() {
function five (line 27) | fn five() {
function nine (line 32) | fn nine() {
function ten (line 37) | fn ten() {
FILE: exercises/07_threads/04_scoped_threads/src/lib.rs
function sum (line 5) | pub fn sum(v: Vec<i32>) -> i32 {
function empty (line 14) | fn empty() {
function one (line 19) | fn one() {
function five (line 24) | fn five() {
function nine (line 29) | fn nine() {
function ten (line 34) | fn ten() {
FILE: exercises/07_threads/05_channels/src/data.rs
type Ticket (line 5) | pub struct Ticket {
type TicketDraft (line 13) | pub struct TicketDraft {
type Status (line 19) | pub enum Status {
FILE: exercises/07_threads/05_channels/src/lib.rs
type Command (line 6) | pub enum Command {
function launch (line 13) | pub fn launch() -> Sender<Command> {
function server (line 23) | pub fn server(receiver: Receiver<Command>) {}
FILE: exercises/07_threads/05_channels/src/store.rs
type TicketId (line 5) | pub struct TicketId(u64);
type TicketStore (line 8) | pub struct TicketStore {
method new (line 14) | pub fn new() -> Self {
method add_ticket (line 21) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
FILE: exercises/07_threads/05_channels/tests/insert.rs
function a_thread_is_spawned (line 9) | fn a_thread_is_spawned() {
function ready (line 24) | fn ready() {
FILE: exercises/07_threads/06_interior_mutability/src/lib.rs
type DropTracker (line 7) | pub struct DropTracker<T> {
function new (line 13) | pub fn new(value: T, counter: todo!()) -> Self {
method drop (line 19) | fn drop(&mut self) {
function it_works (line 29) | fn it_works() {
function multiple (line 36) | fn multiple() {
FILE: exercises/07_threads/07_ack/src/data.rs
type Ticket (line 5) | pub struct Ticket {
type TicketDraft (line 13) | pub struct TicketDraft {
type Status (line 19) | pub enum Status {
FILE: exercises/07_threads/07_ack/src/lib.rs
type Command (line 8) | pub enum Command {
function launch (line 13) | pub fn launch() -> Sender<Command> {
function server (line 20) | pub fn server(receiver: Receiver<Command>) {
FILE: exercises/07_threads/07_ack/src/store.rs
type TicketId (line 5) | pub struct TicketId(u64);
type TicketStore (line 8) | pub struct TicketStore {
method new (line 14) | pub fn new() -> Self {
method add_ticket (line 21) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 34) | pub fn get(&self, id: TicketId) -> Option<&Ticket> {
FILE: exercises/07_threads/07_ack/tests/insert.rs
function insert_works (line 7) | fn insert_works() {
FILE: exercises/07_threads/08_client/src/data.rs
type Ticket (line 5) | pub struct Ticket {
type TicketDraft (line 13) | pub struct TicketDraft {
type Status (line 19) | pub enum Status {
FILE: exercises/07_threads/08_client/src/lib.rs
type TicketStoreClient (line 10) | pub struct TicketStoreClient {}
method insert (line 14) | pub fn insert(&self, draft: TicketDraft) -> TicketId {
method get (line 18) | pub fn get(&self, id: TicketId) -> Option<Ticket> {
function launch (line 23) | pub fn launch() -> TicketStoreClient {
type Command (line 30) | enum Command {
function server (line 41) | fn server(receiver: Receiver<Command>) {
FILE: exercises/07_threads/08_client/src/store.rs
type TicketId (line 5) | pub struct TicketId(u64);
type TicketStore (line 8) | pub struct TicketStore {
method new (line 14) | pub fn new() -> Self {
method add_ticket (line 21) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 34) | pub fn get(&self, id: TicketId) -> Option<&Ticket> {
FILE: exercises/07_threads/08_client/tests/insert.rs
function insert_works (line 6) | fn insert_works() {
FILE: exercises/07_threads/09_bounded/src/data.rs
type Ticket (line 5) | pub struct Ticket {
type TicketDraft (line 13) | pub struct TicketDraft {
type Status (line 19) | pub enum Status {
FILE: exercises/07_threads/09_bounded/src/lib.rs
type TicketStoreClient (line 10) | pub struct TicketStoreClient {
method insert (line 15) | pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, todo!()> {
method get (line 19) | pub fn get(&self, id: TicketId) -> Result<Option<Ticket>, todo!()> {
function launch (line 24) | pub fn launch(capacity: usize) -> TicketStoreClient {
type Command (line 30) | enum Command {
function server (line 41) | pub fn server(receiver: Receiver<Command>) {
FILE: exercises/07_threads/09_bounded/src/store.rs
type TicketId (line 5) | pub struct TicketId(u64);
type TicketStore (line 8) | pub struct TicketStore {
method new (line 14) | pub fn new() -> Self {
method add_ticket (line 21) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 34) | pub fn get(&self, id: TicketId) -> Option<&Ticket> {
FILE: exercises/07_threads/09_bounded/tests/insert.rs
function works (line 6) | fn works() {
FILE: exercises/07_threads/10_patch/src/data.rs
type Ticket (line 5) | pub struct Ticket {
type TicketDraft (line 13) | pub struct TicketDraft {
type TicketPatch (line 19) | pub struct TicketPatch {
type Status (line 27) | pub enum Status {
FILE: exercises/07_threads/10_patch/src/lib.rs
type TicketStoreClient (line 11) | pub struct TicketStoreClient {
method insert (line 16) | pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, Overloade...
method get (line 27) | pub fn get(&self, id: TicketId) -> Result<Option<Ticket>, OverloadedEr...
method update (line 38) | pub fn update(&self, ticket_patch: TicketPatch) -> Result<(), Overload...
type OverloadedError (line 43) | pub struct OverloadedError;
function launch (line 45) | pub fn launch(capacity: usize) -> TicketStoreClient {
type Command (line 51) | enum Command {
function server (line 66) | pub fn server(receiver: Receiver<Command>) {
FILE: exercises/07_threads/10_patch/src/store.rs
type TicketId (line 5) | pub struct TicketId(u64);
type TicketStore (line 8) | pub struct TicketStore {
method new (line 14) | pub fn new() -> Self {
method add_ticket (line 21) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 34) | pub fn get(&self, id: TicketId) -> Option<&Ticket> {
method get_mut (line 38) | pub fn get_mut(&mut self, id: TicketId) -> Option<&mut Ticket> {
FILE: exercises/07_threads/10_patch/tests/check.rs
function works (line 6) | fn works() {
FILE: exercises/07_threads/11_locks/src/data.rs
type Ticket (line 5) | pub struct Ticket {
type TicketDraft (line 13) | pub struct TicketDraft {
type Status (line 19) | pub enum Status {
FILE: exercises/07_threads/11_locks/src/lib.rs
type TicketStoreClient (line 14) | pub struct TicketStoreClient {
method insert (line 19) | pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, Overloade...
method get (line 30) | pub fn get(&self, id: TicketId) -> Result<Option<Arc<Mutex<Ticket>>>, ...
type OverloadedError (line 44) | pub struct OverloadedError;
function launch (line 46) | pub fn launch(capacity: usize) -> TicketStoreClient {
type Command (line 52) | enum Command {
function server (line 63) | pub fn server(receiver: Receiver<Command>) {
FILE: exercises/07_threads/11_locks/src/store.rs
type TicketId (line 6) | pub struct TicketId(u64);
type TicketStore (line 9) | pub struct TicketStore {
method new (line 15) | pub fn new() -> Self {
method add_ticket (line 22) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 37) | pub fn get(&self, id: TicketId) -> Option<todo!()> {
FILE: exercises/07_threads/11_locks/tests/check.rs
function works (line 6) | fn works() {
FILE: exercises/07_threads/12_rw_lock/src/data.rs
type Ticket (line 5) | pub struct Ticket {
type TicketDraft (line 13) | pub struct TicketDraft {
type Status (line 19) | pub enum Status {
FILE: exercises/07_threads/12_rw_lock/src/lib.rs
type TicketStoreClient (line 13) | pub struct TicketStoreClient {
method insert (line 18) | pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, Overloade...
method get (line 29) | pub fn get(&self, id: TicketId) -> Result<Option<Arc<Mutex<Ticket>>>, ...
type OverloadedError (line 43) | pub struct OverloadedError;
function launch (line 45) | pub fn launch(capacity: usize) -> TicketStoreClient {
type Command (line 51) | enum Command {
function server (line 62) | pub fn server(receiver: Receiver<Command>) {
FILE: exercises/07_threads/12_rw_lock/src/store.rs
type TicketId (line 6) | pub struct TicketId(u64);
type TicketStore (line 9) | pub struct TicketStore {
method new (line 15) | pub fn new() -> Self {
method add_ticket (line 22) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 38) | pub fn get(&self, id: TicketId) -> Option<Arc<Mutex<Ticket>>> {
FILE: exercises/07_threads/12_rw_lock/tests/check.rs
function works (line 6) | fn works() {
FILE: exercises/07_threads/13_without_channels/src/data.rs
type Ticket (line 5) | pub struct Ticket {
type TicketDraft (line 13) | pub struct TicketDraft {
type Status (line 19) | pub enum Status {
FILE: exercises/07_threads/13_without_channels/src/store.rs
type TicketId (line 7) | pub struct TicketId(u64);
type TicketStore (line 10) | pub struct TicketStore {
method new (line 16) | pub fn new() -> Self {
method add_ticket (line 23) | pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
method get (line 37) | pub fn get(&self, id: TicketId) -> Option<Arc<RwLock<Ticket>>> {
FILE: exercises/07_threads/13_without_channels/tests/check.rs
function works (line 9) | fn works() {
FILE: exercises/07_threads/14_sync/src/lib.rs
function outro (line 2) | fn outro() -> &'static str {
function test_outro (line 11) | fn test_outro() {
FILE: exercises/08_futures/00_intro/src/lib.rs
function intro (line 1) | fn intro() -> &'static str {
function test_intro (line 11) | fn test_intro() {
FILE: exercises/08_futures/01_async_fn/src/lib.rs
function echo (line 13) | pub async fn echo(listener: TcpListener) -> Result<(), anyhow::Error> {
function test_echo (line 23) | async fn test_echo() {
FILE: exercises/08_futures/02_spawn/src/lib.rs
function echoes (line 6) | pub async fn echoes(first: TcpListener, second: TcpListener) -> Result<(...
function bind_random (line 18) | async fn bind_random() -> (TcpListener, SocketAddr) {
function test_echo (line 25) | async fn test_echo() {
FILE: exercises/08_futures/03_runtime/src/lib.rs
function fixed_reply (line 8) | pub async fn fixed_reply<T>(first: TcpListener, second: TcpListener, rep...
function bind_random (line 24) | async fn bind_random() -> (TcpListener, SocketAddr) {
function test_echo (line 31) | async fn test_echo() {
FILE: exercises/08_futures/04_future/src/lib.rs
function spawner (line 8) | fn spawner() {
function example (line 12) | async fn example() {
FILE: exercises/08_futures/05_blocking/src/lib.rs
function echo (line 8) | pub async fn echo(listener: TcpListener) -> Result<(), anyhow::Error> {
function bind_random (line 27) | async fn bind_random() -> (TcpListener, SocketAddr) {
function test_echo (line 34) | async fn test_echo() {
FILE: exercises/08_futures/06_async_aware_primitives/src/lib.rs
type Message (line 9) | pub struct Message {
function pong (line 16) | pub async fn pong(mut receiver: mpsc::Receiver<Message>) {
function ping (line 38) | async fn ping() {
FILE: exercises/08_futures/07_cancellation/src/lib.rs
function run (line 7) | pub async fn run(listener: TcpListener, n_messages: usize, timeout: Dura...
function ping (line 25) | async fn ping() {
FILE: helpers/common/src/lib.rs
function overly_long_description (line 1) | pub fn overly_long_description() -> String {
function overly_long_title (line 5) | pub fn overly_long_title() -> String {
function valid_title (line 9) | pub fn valid_title() -> String {
function valid_description (line 13) | pub fn valid_description() -> String {
FILE: helpers/mdbook-exercise-linker/src/lib.rs
type ExerciseLinker (line 6) | pub struct ExerciseLinker;
method new (line 9) | pub fn new() -> ExerciseLinker {
method name (line 15) | fn name(&self) -> &str {
method run (line 19) | fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book,...
method supports_renderer (line 39) | fn supports_renderer(&self, _renderer: &str) -> bool {
function process_book_item (line 44) | fn process_book_item(item: &mut BookItem, renderer: &str, root_url: &str) {
FILE: helpers/mdbook-exercise-linker/src/main.rs
function make_app (line 11) | pub fn make_app() -> Command {
function main (line 19) | fn main() {
function handle_preprocessing (line 33) | fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> {
function handle_supports (line 55) | fn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! {
FILE: helpers/mdbook-link-shortener/src/lib.rs
type LinkShortener (line 12) | pub struct LinkShortener;
method new (line 57) | pub fn new() -> LinkShortener {
type AliasGenerator (line 14) | struct AliasGenerator {
constant ALPHABET (line 19) | const ALPHABET: &'static [u8] = b"f2z4x6v8bnm3q5w7e9rtyuplkshgjdca";
method new (line 21) | fn new() -> AliasGenerator {
method next (line 28) | fn next(&mut self) -> String {
method next_until_unique (line 47) | fn next_until_unique(&mut self, link2alias: &BiHashMap<String, String>...
method name (line 63) | fn name(&self) -> &str {
method run (line 67) | fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book,...
method supports_renderer (line 142) | fn supports_renderer(&self, _renderer: &str) -> bool {
function replace_anchors (line 147) | fn replace_anchors(
FILE: helpers/mdbook-link-shortener/src/main.rs
type Cli (line 11) | pub struct Cli {
type SubCommand (line 17) | pub enum SubCommand {
type Supports (line 23) | pub struct Supports {
function main (line 28) | fn main() -> Result<(), anyhow::Error> {
function handle_preprocessing (line 46) | fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> {
FILE: helpers/ticket_fields/src/description.rs
type TicketDescription (line 2) | pub struct TicketDescription(String);
type Error (line 13) | type Error = TicketDescriptionError;
method try_from (line 15) | fn try_from(value: String) -> Result<Self, Self::Error> {
type Error (line 22) | type Error = TicketDescriptionError;
method try_from (line 24) | fn try_from(value: &str) -> Result<Self, Self::Error> {
type TicketDescriptionError (line 5) | pub enum TicketDescriptionError {
function validate (line 30) | fn validate(description: &str) -> Result<(), TicketDescriptionError> {
function test_try_from_string (line 47) | fn test_try_from_string() {
function test_try_from_empty_string (line 54) | fn test_try_from_empty_string() {
function test_try_from_long_string (line 60) | fn test_try_from_long_string() {
function test_try_from_str (line 69) | fn test_try_from_str() {
FILE: helpers/ticket_fields/src/test_helpers.rs
function ticket_title (line 6) | pub fn ticket_title() -> TicketTitle {
function ticket_description (line 12) | pub fn ticket_description() -> TicketDescription {
FILE: helpers/ticket_fields/src/title.rs
type TicketTitle (line 4) | pub struct TicketTitle(String);
type Error (line 15) | type Error = TicketTitleError;
method try_from (line 17) | fn try_from(value: String) -> Result<Self, Self::Error> {
type Error (line 24) | type Error = TicketTitleError;
method try_from (line 26) | fn try_from(value: &str) -> Result<Self, Self::Error> {
type TicketTitleError (line 7) | pub enum TicketTitleError {
function validate (line 32) | fn validate(title: &str) -> Result<(), TicketTitleError> {
function test_try_from_string (line 49) | fn test_try_from_string() {
function test_try_from_empty_string (line 56) | fn test_try_from_empty_string() {
function test_try_from_long_string (line 62) | fn test_try_from_long_string() {
function test_try_from_str (line 68) | fn test_try_from_str() {
Condensed preview — 247 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (238K chars).
[
{
"path": ".github/workflows/ci.yml",
"chars": 5575,
"preview": "name: \"CI\"\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n workflow_dispatch:\n sch"
},
{
"path": ".gitignore",
"chars": 30,
"preview": "target/\nexercises/progress.db\n"
},
{
"path": ".wr.toml",
"chars": 0,
"preview": ""
},
{
"path": "Cargo.toml",
"chars": 390,
"preview": "[workspace]\nmembers = [\n \"exercises/*/*\",\n \"helpers/common\",\n \"helpers/mdbook-exercise-linker\",\n \"helpers/mdbook-lin"
},
{
"path": "README.md",
"chars": 1821,
"preview": "# Learn Rust, one exercise at a time\n\nYou've heard about Rust, but you never had the chance to try it out?\\\nThis course "
},
{
"path": "dprint.json",
"chars": 183,
"preview": "{\n \"markdown\": {\n },\n \"toml\": {\n },\n \"excludes\": [],\n \"plugins\": [\n \"https://plugins.dprint.dev/markdown-0.17.0"
},
{
"path": "exercises/01_intro/00_welcome/Cargo.toml",
"chars": 262,
"preview": "[package]\nname = \"welcome_00\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the t"
},
{
"path": "exercises/01_intro/00_welcome/src/lib.rs",
"chars": 2109,
"preview": "// This is a Rust file. It is a plain text file with a `.rs` extension.\n//\n// Like most modern programming languages, Ru"
},
{
"path": "exercises/01_intro/01_syntax/Cargo.toml",
"chars": 258,
"preview": "[package]\nname = \"syntax\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the time "
},
{
"path": "exercises/01_intro/01_syntax/src/lib.rs",
"chars": 512,
"preview": "// TODO: fix the function signature below to make the tests pass.\n// Make sure to read the compiler error message—the R"
},
{
"path": "exercises/02_basic_calculator/00_intro/Cargo.toml",
"chars": 260,
"preview": "[package]\nname = \"intro_01\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the tim"
},
{
"path": "exercises/02_basic_calculator/00_intro/src/lib.rs",
"chars": 240,
"preview": "fn intro() -> &'static str {\n // TODO: fix me 👇\n \"I'm ready to __!\"\n}\n\n#[cfg(test)]\nmod tests {\n use crate::int"
},
{
"path": "exercises/02_basic_calculator/01_integers/Cargo.toml",
"chars": 260,
"preview": "[package]\nname = \"integers\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the tim"
},
{
"path": "exercises/02_basic_calculator/01_integers/src/lib.rs",
"chars": 300,
"preview": "fn compute(a: u32, b: u32) -> u32 {\n // TODO: change the line below to fix the compiler error and make the tests pass"
},
{
"path": "exercises/02_basic_calculator/02_variables/Cargo.toml",
"chars": 261,
"preview": "[package]\nname = \"variables\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the ti"
},
{
"path": "exercises/02_basic_calculator/02_variables/src/lib.rs",
"chars": 1036,
"preview": "// 👇 The lines below, starting with `///`, are called **documentation comments**.\n// They attach documentation to the"
},
{
"path": "exercises/02_basic_calculator/03_if_else/Cargo.toml",
"chars": 259,
"preview": "[package]\nname = \"if_else\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the time"
},
{
"path": "exercises/02_basic_calculator/03_if_else/src/lib.rs",
"chars": 572,
"preview": "/// Return `12` if `n` is even,\n/// `13` if `n` is divisible by `3`,\n/// `17` otherwise.\nfn magic_number(n: u32) -> u32 "
},
{
"path": "exercises/02_basic_calculator/04_panics/Cargo.toml",
"chars": 258,
"preview": "[package]\nname = \"panics\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the time "
},
{
"path": "exercises/02_basic_calculator/04_panics/src/lib.rs",
"chars": 813,
"preview": "/// Given the start and end points of a journey, and the time it took to complete the journey,\n/// calculate the average"
},
{
"path": "exercises/02_basic_calculator/05_factorial/Cargo.toml",
"chars": 261,
"preview": "[package]\nname = \"factorial\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the ti"
},
{
"path": "exercises/02_basic_calculator/05_factorial/src/lib.rs",
"chars": 871,
"preview": "// Define a function named `factorial` that, given a non-negative integer `n`,\n// returns `n!`, the factorial of `n`.\n//"
},
{
"path": "exercises/02_basic_calculator/06_while/Cargo.toml",
"chars": 258,
"preview": "[package]\nname = \"while_\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the time "
},
{
"path": "exercises/02_basic_calculator/06_while/src/lib.rs",
"chars": 633,
"preview": "// Rewrite the factorial function using a `while` loop.\npub fn factorial(n: u32) -> u32 {\n // The `todo!()` macro is "
},
{
"path": "exercises/02_basic_calculator/07_for/Cargo.toml",
"chars": 256,
"preview": "[package]\nname = \"for_\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the time be"
},
{
"path": "exercises/02_basic_calculator/07_for/src/lib.rs",
"chars": 451,
"preview": "// Rewrite the factorial function using a `for` loop.\npub fn factorial(n: u32) -> u32 {\n todo!()\n}\n\n#[cfg(test)]\nmod "
},
{
"path": "exercises/02_basic_calculator/08_overflow/Cargo.toml",
"chars": 260,
"preview": "[package]\nname = \"overflow\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the tim"
},
{
"path": "exercises/02_basic_calculator/08_overflow/src/lib.rs",
"chars": 1230,
"preview": "// Customize the `dev` profile to wrap around on overflow.\n// Check Cargo's documentation to find out the right syntax:\n"
},
{
"path": "exercises/02_basic_calculator/09_saturating/Cargo.toml",
"chars": 262,
"preview": "[package]\nname = \"saturating\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the t"
},
{
"path": "exercises/02_basic_calculator/09_saturating/src/lib.rs",
"chars": 685,
"preview": "pub fn factorial(n: u32) -> u32 {\n let mut result = 1;\n for i in 1..=n {\n // Use saturating multiplication "
},
{
"path": "exercises/02_basic_calculator/10_as_casting/Cargo.toml",
"chars": 259,
"preview": "[package]\nname = \"as_cast\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the time"
},
{
"path": "exercises/02_basic_calculator/10_as_casting/src/lib.rs",
"chars": 1184,
"preview": "// TODO: based on what you learned in this section, replace `todo!()` with\n// the correct value after the conversion.\n\n"
},
{
"path": "exercises/03_ticket_v1/00_intro/Cargo.toml",
"chars": 260,
"preview": "[package]\nname = \"intro_02\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the tim"
},
{
"path": "exercises/03_ticket_v1/00_intro/src/lib.rs",
"chars": 247,
"preview": "fn intro() -> &'static str {\n // TODO: fix me 👇\n \"I'm ready to __!\"\n}\n\n#[cfg(test)]\nmod tests {\n use crate::int"
},
{
"path": "exercises/03_ticket_v1/01_struct/Cargo.toml",
"chars": 259,
"preview": "[package]\nname = \"struct_\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the time"
},
{
"path": "exercises/03_ticket_v1/01_struct/src/lib.rs",
"chars": 680,
"preview": "// Define a struct named `Order` with the following fields:\n// - `price`, an unsigned integer\n// - `quantity`, an unsign"
},
{
"path": "exercises/03_ticket_v1/02_validation/Cargo.toml",
"chars": 328,
"preview": "[package]\nname = \"validation\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helpers"
},
{
"path": "exercises/03_ticket_v1/02_validation/src/lib.rs",
"chars": 2461,
"preview": "struct Ticket {\n title: String,\n description: String,\n status: String,\n}\n\nimpl Ticket {\n // TODO: implement "
},
{
"path": "exercises/03_ticket_v1/03_modules/Cargo.toml",
"chars": 259,
"preview": "[package]\nname = \"modules\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lints.rust]\n# We silence dead code warnings for the time"
},
{
"path": "exercises/03_ticket_v1/03_modules/src/lib.rs",
"chars": 1140,
"preview": "mod helpers {\n // TODO: Make this code compile, either by adding a `use` statement or by using\n // the appropriat"
},
{
"path": "exercises/03_ticket_v1/04_visibility/Cargo.toml",
"chars": 65,
"preview": "[package]\nname = \"visibility\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/03_ticket_v1/04_visibility/src/lib.rs",
"chars": 2961,
"preview": "mod ticket {\n struct Ticket {\n title: String,\n description: String,\n status: String,\n }\n\n "
},
{
"path": "exercises/03_ticket_v1/05_encapsulation/Cargo.toml",
"chars": 68,
"preview": "[package]\nname = \"encapsulation\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/03_ticket_v1/05_encapsulation/src/lib.rs",
"chars": 1828,
"preview": "pub mod ticket {\n pub struct Ticket {\n title: String,\n description: String,\n status: String,\n "
},
{
"path": "exercises/03_ticket_v1/06_ownership/Cargo.toml",
"chars": 64,
"preview": "[package]\nname = \"ownership\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/03_ticket_v1/06_ownership/src/lib.rs",
"chars": 1879,
"preview": "// TODO: based on what we just learned about ownership, it sounds like immutable references\n// are a good fit for our "
},
{
"path": "exercises/03_ticket_v1/07_setters/Cargo.toml",
"chars": 128,
"preview": "[package]\nname = \"setters\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helpers/co"
},
{
"path": "exercises/03_ticket_v1/07_setters/src/lib.rs",
"chars": 3136,
"preview": "// TODO: Add &mut-setters to the `Ticket` struct for each of its fields.\n// Make sure to enforce the same validation r"
},
{
"path": "exercises/03_ticket_v1/08_stack/Cargo.toml",
"chars": 60,
"preview": "[package]\nname = \"stack\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/03_ticket_v1/08_stack/src/lib.rs",
"chars": 445,
"preview": "// TODO: based on what you learned in this section, replace `todo!()` with\n// the correct **stack size** for the respec"
},
{
"path": "exercises/03_ticket_v1/09_heap/Cargo.toml",
"chars": 59,
"preview": "[package]\nname = \"heap\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/03_ticket_v1/09_heap/src/lib.rs",
"chars": 851,
"preview": "pub struct Ticket {\n title: String,\n description: String,\n status: String,\n}\n\n// TODO: based on what you learne"
},
{
"path": "exercises/03_ticket_v1/10_references_in_memory/Cargo.toml",
"chars": 75,
"preview": "[package]\nname = \"references_in_memory\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/03_ticket_v1/10_references_in_memory/src/lib.rs",
"chars": 582,
"preview": "pub struct Ticket {\n title: String,\n description: String,\n status: String,\n}\n\n// TODO: based on what you learne"
},
{
"path": "exercises/03_ticket_v1/11_destructor/Cargo.toml",
"chars": 65,
"preview": "[package]\nname = \"destructor\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/03_ticket_v1/11_destructor/src/lib.rs",
"chars": 420,
"preview": "// We need some more machinery to write a proper exercise for destructors.\n// We'll pick the concept up again in a later"
},
{
"path": "exercises/03_ticket_v1/12_outro/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"outro_02\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/03_ticket_v1/12_outro/src/lib.rs",
"chars": 911,
"preview": "// TODO: Define a new `Order` type.\n// It should keep track of three pieces of information: `product_name`, `quantity`"
},
{
"path": "exercises/03_ticket_v1/12_outro/tests/integration.rs",
"chars": 1180,
"preview": "use outro_02::Order;\n\n// Files inside the `tests` directory are only compiled when you run tests.\n// As a consequence, w"
},
{
"path": "exercises/04_traits/00_intro/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"intro_03\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/00_intro/src/lib.rs",
"chars": 232,
"preview": "fn intro() -> &'static str {\n // TODO: fix me 👇\n \"I'm ready to __!\"\n}\n\n#[cfg(test)]\nmod tests {\n use crate::int"
},
{
"path": "exercises/04_traits/01_trait/Cargo.toml",
"chars": 61,
"preview": "[package]\nname = \"trait_\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/01_trait/src/lib.rs",
"chars": 524,
"preview": "// Define a trait named `IsEven` that has a method `is_even` that returns a `true` if `self` is\n// even, otherwise `fals"
},
{
"path": "exercises/04_traits/02_orphan_rule/Cargo.toml",
"chars": 61,
"preview": "[package]\nname = \"orphan\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/02_orphan_rule/src/lib.rs",
"chars": 394,
"preview": "// TODO: this is an example of an orphan rule violation.\n// We're implementing a foreign trait (`PartialEq`, from `std`"
},
{
"path": "exercises/04_traits/03_operator_overloading/Cargo.toml",
"chars": 66,
"preview": "[package]\nname = \"overloading\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/03_operator_overloading/src/lib.rs",
"chars": 2278,
"preview": "use std::cmp::PartialEq;\n\nstruct Ticket {\n title: String,\n description: String,\n status: String,\n}\n\n// TODO: Im"
},
{
"path": "exercises/04_traits/04_derive/Cargo.toml",
"chars": 62,
"preview": "[package]\nname = \"derives\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/04_derive/src/lib.rs",
"chars": 2675,
"preview": "// TODO: A (derivable) trait implementation is missing for this exercise to compile successfully.\n// Fix it!\n//\n// # `"
},
{
"path": "exercises/04_traits/05_trait_bounds/Cargo.toml",
"chars": 67,
"preview": "[package]\nname = \"trait_bounds\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/05_trait_bounds/src/lib.rs",
"chars": 573,
"preview": "// TODO: Add the necessary trait bounds to `min` so that it compiles successfully.\n// Refer to the documentation of th"
},
{
"path": "exercises/04_traits/06_str_slice/Cargo.toml",
"chars": 130,
"preview": "[package]\nname = \"str_slice\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helpers/"
},
{
"path": "exercises/04_traits/06_str_slice/src/lib.rs",
"chars": 1702,
"preview": "// TODO: Re-implement `Ticket`'s accessor methods. This time return a `&str` rather than a `&String`.\n\npub struct Ticket"
},
{
"path": "exercises/04_traits/07_deref/Cargo.toml",
"chars": 60,
"preview": "[package]\nname = \"deref\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/07_deref/src/lib.rs",
"chars": 983,
"preview": "// TODO: whenever `title` and `description` are returned via their accessor methods, they\n// should be normalized—i.e."
},
{
"path": "exercises/04_traits/08_sized/Cargo.toml",
"chars": 60,
"preview": "[package]\nname = \"sized\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/08_sized/src/lib.rs",
"chars": 264,
"preview": "pub fn example() {\n // Trying to get the size of a str (or any other DST)\n // via `std::mem::size_of` will result "
},
{
"path": "exercises/04_traits/09_from/Cargo.toml",
"chars": 59,
"preview": "[package]\nname = \"from\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/09_from/src/lib.rs",
"chars": 237,
"preview": "// TODO: Implement the `From` trait for the `WrappingU32` type to make `example` compile.\n\npub struct WrappingU32 {\n "
},
{
"path": "exercises/04_traits/10_assoc_vs_generic/Cargo.toml",
"chars": 71,
"preview": "[package]\nname = \"assoc_vs_generic\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/10_assoc_vs_generic/src/lib.rs",
"chars": 1218,
"preview": "// TODO: Define a new trait, `Power`, that has a method `power` that raises `self`\n// to the power of `n`.\n// The trai"
},
{
"path": "exercises/04_traits/11_clone/Cargo.toml",
"chars": 60,
"preview": "[package]\nname = \"clone\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/11_clone/src/lib.rs",
"chars": 512,
"preview": "// TODO: add the necessary `Clone` implementations (and invocations)\n// to get the code to compile.\n\npub fn summary(tic"
},
{
"path": "exercises/04_traits/12_copy/Cargo.toml",
"chars": 59,
"preview": "[package]\nname = \"copy\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/12_copy/src/lib.rs",
"chars": 503,
"preview": "// TODO: implement the necessary traits to make the test compile and pass.\n// You *can't* modify the test.\n\npub struct "
},
{
"path": "exercises/04_traits/13_drop/Cargo.toml",
"chars": 59,
"preview": "[package]\nname = \"drop\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/13_drop/src/lib.rs",
"chars": 582,
"preview": "// TODO: implement a so-called \"Drop bomb\": a type that panics when dropped\n// unless a certain operation has been perf"
},
{
"path": "exercises/04_traits/14_outro/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"outro_03\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/04_traits/14_outro/src/lib.rs",
"chars": 575,
"preview": "// TODO: Define a new `SaturatingU16` type.\n// It should hold a `u16` value.\n// It should provide conversions from `"
},
{
"path": "exercises/04_traits/14_outro/tests/integration.rs",
"chars": 526,
"preview": "use outro_03::SaturatingU16;\n\n#[test]\nfn test_saturating_u16() {\n let a: SaturatingU16 = (&10u8).into();\n let b: S"
},
{
"path": "exercises/05_ticket_v2/00_intro/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"intro_04\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/05_ticket_v2/00_intro/src/lib.rs",
"chars": 238,
"preview": "fn intro() -> &'static str {\n // TODO: fix me 👇\n \"I'm ready to __!\"\n}\n\n#[cfg(test)]\nmod tests {\n use crate::int"
},
{
"path": "exercises/05_ticket_v2/01_enum/Cargo.toml",
"chars": 126,
"preview": "[package]\nname = \"enum_\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helpers/comm"
},
{
"path": "exercises/05_ticket_v2/01_enum/src/lib.rs",
"chars": 3452,
"preview": "// TODO: use `Status` as type for `Ticket::status`\n// Adjust the signature and implementation of all other methods as "
},
{
"path": "exercises/05_ticket_v2/02_match/Cargo.toml",
"chars": 61,
"preview": "[package]\nname = \"match_\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/05_ticket_v2/02_match/src/lib.rs",
"chars": 725,
"preview": "enum Shape {\n Circle,\n Square,\n Rectangle,\n Triangle,\n Pentagon,\n}\n\nimpl Shape {\n // TODO: Implement t"
},
{
"path": "exercises/05_ticket_v2/03_variants_with_data/Cargo.toml",
"chars": 139,
"preview": "[package]\nname = \"variants_with_data\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../.."
},
{
"path": "exercises/05_ticket_v2/03_variants_with_data/src/lib.rs",
"chars": 1900,
"preview": "// TODO: Implement `Ticket::assigned_to`.\n// Return the name of the person assigned to the ticket, if the ticket is in "
},
{
"path": "exercises/05_ticket_v2/04_if_let/Cargo.toml",
"chars": 61,
"preview": "[package]\nname = \"if_let\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/05_ticket_v2/04_if_let/src/lib.rs",
"chars": 719,
"preview": "enum Shape {\n Circle { radius: f64 },\n Square { border: f64 },\n Rectangle { width: f64, height: f64 },\n}\n\nimpl "
},
{
"path": "exercises/05_ticket_v2/05_nullability/Cargo.toml",
"chars": 132,
"preview": "[package]\nname = \"nullability\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helper"
},
{
"path": "exercises/05_ticket_v2/05_nullability/src/lib.rs",
"chars": 1717,
"preview": "// TODO: Implement `Ticket::assigned_to` using `Option` as the return type.\n\n#[derive(Debug, PartialEq)]\nstruct Ticket {"
},
{
"path": "exercises/05_ticket_v2/06_fallibility/Cargo.toml",
"chars": 132,
"preview": "[package]\nname = \"fallibility\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helper"
},
{
"path": "exercises/05_ticket_v2/06_fallibility/src/lib.rs",
"chars": 1979,
"preview": "// TODO: Convert the `Ticket::new` method to return a `Result` instead of panicking.\n// Use `String` as the error type"
},
{
"path": "exercises/05_ticket_v2/07_unwrap/Cargo.toml",
"chars": 127,
"preview": "[package]\nname = \"unwrap\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helpers/com"
},
{
"path": "exercises/05_ticket_v2/07_unwrap/src/lib.rs",
"chars": 2149,
"preview": "// TODO: `easy_ticket` should panic when the title is invalid.\n// When the description is invalid, instead, it should "
},
{
"path": "exercises/05_ticket_v2/08_error_enums/Cargo.toml",
"chars": 132,
"preview": "[package]\nname = \"error_enums\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helper"
},
{
"path": "exercises/05_ticket_v2/08_error_enums/src/lib.rs",
"chars": 2544,
"preview": "// TODO: Use two variants, one for a title error and one for a description error.\n// Each variant should contain a str"
},
{
"path": "exercises/05_ticket_v2/09_error_trait/Cargo.toml",
"chars": 160,
"preview": "[package]\nname = \"error_trait\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\ncommon = { path = \"../../../helper"
},
{
"path": "exercises/05_ticket_v2/09_error_trait/src/lib.rs",
"chars": 3262,
"preview": "// TODO: Implement `Debug`, `Display` and `Error` for the `TicketNewError` enum.\n// When implementing `Display`, you ma"
},
{
"path": "exercises/05_ticket_v2/10_packages/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"packages\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/05_ticket_v2/10_packages/src/main.rs",
"chars": 409,
"preview": "// This is a `main.rs` file, therefore `cargo` interprets this as the root of a binary target.\n\n// TODO: fix this broken"
},
{
"path": "exercises/05_ticket_v2/11_dependencies/Cargo.toml",
"chars": 59,
"preview": "[package]\nname = \"deps\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/05_ticket_v2/11_dependencies/src/lib.rs",
"chars": 235,
"preview": "// TODO: Add `anyhow` as a dependency of this project.\n// Don't touch this import!\n\n// When you import a type (`Error`)"
},
{
"path": "exercises/05_ticket_v2/12_thiserror/Cargo.toml",
"chars": 147,
"preview": "[package]\nname = \"thiserror_\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n\n[dev-dependencies]\ncommon = { path = \""
},
{
"path": "exercises/05_ticket_v2/12_thiserror/src/lib.rs",
"chars": 2391,
"preview": "// TODO: Implement the `Error` trait for `TicketNewError` using `thiserror`.\n// We've changed the enum variants to be "
},
{
"path": "exercises/05_ticket_v2/13_try_from/Cargo.toml",
"chars": 78,
"preview": "[package]\nname = \"tryfrom\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n"
},
{
"path": "exercises/05_ticket_v2/13_try_from/src/lib.rs",
"chars": 1031,
"preview": "// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for `Status`.\n// The parsing should be case-insensitive.\n\n#[de"
},
{
"path": "exercises/05_ticket_v2/14_source/Cargo.toml",
"chars": 164,
"preview": "[package]\nname = \"source\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nthiserror = \"1.0.69\"\n\n[dev-dependencies]\nco"
},
{
"path": "exercises/05_ticket_v2/14_source/src/lib.rs",
"chars": 2391,
"preview": "use crate::status::Status;\n\n// We've seen how to declare modules in one of the earliest exercises, but\n// we haven't see"
},
{
"path": "exercises/05_ticket_v2/14_source/src/status.rs",
"chars": 1199,
"preview": "#[derive(Debug, PartialEq, Clone)]\npub enum Status {\n ToDo,\n InProgress,\n Done,\n}\n\nimpl TryFrom<String> for Sta"
},
{
"path": "exercises/05_ticket_v2/15_outro/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"outro_04\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/05_ticket_v2/15_outro/src/description.rs",
"chars": 2072,
"preview": "// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `TicketDescription` type,\n// enforcing that the descr"
},
{
"path": "exercises/05_ticket_v2/15_outro/src/lib.rs",
"chars": 1076,
"preview": "// TODO: you have something to do in each of the modules in this crate!\nmod description;\nmod status;\nmod title;\n\n// A co"
},
{
"path": "exercises/05_ticket_v2/15_outro/src/status.rs",
"chars": 1145,
"preview": "// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `Status` enum.\n// The parsing should be case-insensiti"
},
{
"path": "exercises/05_ticket_v2/15_outro/src/title.rs",
"chars": 1191,
"preview": "// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for the `TicketTitle` type,\n// enforcing that the title is no"
},
{
"path": "exercises/06_ticket_management/00_intro/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"intro_05\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/06_ticket_management/00_intro/src/lib.rs",
"chars": 246,
"preview": "fn intro() -> &'static str {\n // TODO: fix me 👇\n \"I'm ready to __!\"\n}\n\n#[cfg(test)]\nmod tests {\n use crate::int"
},
{
"path": "exercises/06_ticket_management/01_arrays/Cargo.toml",
"chars": 61,
"preview": "[package]\nname = \"arrays\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/06_ticket_management/01_arrays/src/lib.rs",
"chars": 2636,
"preview": "// TODO: Flesh out the `WeekTemperatures` struct and its method implementations to pass the tests.\n\npub struct WeekTempe"
},
{
"path": "exercises/06_ticket_management/02_vec/Cargo.toml",
"chars": 58,
"preview": "[package]\nname = \"vec\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/06_ticket_management/02_vec/src/lib.rs",
"chars": 1154,
"preview": "// Given a number `n`, return the `n+1`th number in the Fibonacci sequence.\n//\n// The Fibonacci sequence is defined as f"
},
{
"path": "exercises/06_ticket_management/03_resizing/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"resizing\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/06_ticket_management/03_resizing/src/lib.rs",
"chars": 519,
"preview": "#[cfg(test)]\nmod tests {\n #[test]\n fn resizing() {\n let mut v = Vec::with_capacity(2);\n v.push(1);\n "
},
{
"path": "exercises/06_ticket_management/04_iterators/Cargo.toml",
"chars": 140,
"preview": "[package]\nname = \"iterators\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helpe"
},
{
"path": "exercises/06_ticket_management/04_iterators/src/lib.rs",
"chars": 1747,
"preview": "use ticket_fields::{TicketDescription, TicketTitle};\n\n// TODO: Let's start sketching our ticket store!\n// First task: i"
},
{
"path": "exercises/06_ticket_management/05_iter/Cargo.toml",
"chars": 135,
"preview": "[package]\nname = \"iter\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helpers/ti"
},
{
"path": "exercises/06_ticket_management/05_iter/src/lib.rs",
"chars": 1639,
"preview": "use ticket_fields::{TicketDescription, TicketTitle};\n\n// TODO: Provide an `iter` method that returns an iterator over `&"
},
{
"path": "exercises/06_ticket_management/06_lifetimes/Cargo.toml",
"chars": 139,
"preview": "[package]\nname = \"lifetime\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helper"
},
{
"path": "exercises/06_ticket_management/06_lifetimes/src/lib.rs",
"chars": 1530,
"preview": "use ticket_fields::{TicketDescription, TicketTitle};\n\n// TODO: Implement the `IntoIterator` trait for `&TicketStore` so "
},
{
"path": "exercises/06_ticket_management/07_combinators/Cargo.toml",
"chars": 142,
"preview": "[package]\nname = \"combinators\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../hel"
},
{
"path": "exercises/06_ticket_management/07_combinators/src/lib.rs",
"chars": 1443,
"preview": "// TODO: Implement the `to_dos` method. It must return a `Vec` of references to the tickets\n// in `TicketStore` with st"
},
{
"path": "exercises/06_ticket_management/08_impl_trait/Cargo.toml",
"chars": 141,
"preview": "[package]\nname = \"impl_trait\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../help"
},
{
"path": "exercises/06_ticket_management/08_impl_trait/src/lib.rs",
"chars": 1526,
"preview": "// TODO: Implement the `in_progress` method. It must return an iterator over the tickets in\n// `TicketStore` with statu"
},
{
"path": "exercises/06_ticket_management/09_impl_trait_2/Cargo.toml",
"chars": 143,
"preview": "[package]\nname = \"impl_trait_2\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../he"
},
{
"path": "exercises/06_ticket_management/09_impl_trait_2/src/lib.rs",
"chars": 1883,
"preview": "// TODO: Rework the signature of `TicketStore::add_ticket` to use a generic type parameter rather\n// than `impl Trait` "
},
{
"path": "exercises/06_ticket_management/10_slices/Cargo.toml",
"chars": 60,
"preview": "[package]\nname = \"slice\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/06_ticket_management/10_slices/src/lib.rs",
"chars": 599,
"preview": "// TODO: Define a function named `sum` that takes a reference to a slice of `u32` and returns the sum of all\n// element"
},
{
"path": "exercises/06_ticket_management/11_mutable_slices/Cargo.toml",
"chars": 64,
"preview": "[package]\nname = \"mut_slice\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/06_ticket_management/11_mutable_slices/src/lib.rs",
"chars": 553,
"preview": "// TODO: Define a function named `squared` that raises all `i32`s within a slice to the power of 2.\n// The slice should"
},
{
"path": "exercises/06_ticket_management/12_two_states/Cargo.toml",
"chars": 141,
"preview": "[package]\nname = \"two_states\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../help"
},
{
"path": "exercises/06_ticket_management/12_two_states/src/lib.rs",
"chars": 2030,
"preview": "// TODO: Update `add_ticket`'s signature: it should take a `TicketDraft` as input\n// and return a `TicketId` as output."
},
{
"path": "exercises/06_ticket_management/13_index/Cargo.toml",
"chars": 136,
"preview": "[package]\nname = \"index\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helpers/t"
},
{
"path": "exercises/06_ticket_management/13_index/src/lib.rs",
"chars": 2148,
"preview": "// TODO: Implement `Index<&TicketId>` and `Index<TicketId>` for `TicketStore`.\n\nuse ticket_fields::{TicketDescription, T"
},
{
"path": "exercises/06_ticket_management/14_index_mut/Cargo.toml",
"chars": 140,
"preview": "[package]\nname = \"index_mut\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helpe"
},
{
"path": "exercises/06_ticket_management/14_index_mut/src/lib.rs",
"chars": 2575,
"preview": "// TODO: Implement `IndexMut<&TicketId>` and `IndexMut<TicketId>` for `TicketStore`.\n\nuse std::ops::Index;\nuse ticket_fi"
},
{
"path": "exercises/06_ticket_management/15_hashmap/Cargo.toml",
"chars": 138,
"preview": "[package]\nname = \"hashmap\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helpers"
},
{
"path": "exercises/06_ticket_management/15_hashmap/src/lib.rs",
"chars": 2853,
"preview": "// TODO: Replace `todo!()`s with the correct implementation.\n// Implement additional traits on `TicketId` if needed.\n\nu"
},
{
"path": "exercises/06_ticket_management/16_btreemap/Cargo.toml",
"chars": 139,
"preview": "[package]\nname = \"btreemap\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helper"
},
{
"path": "exercises/06_ticket_management/16_btreemap/src/lib.rs",
"chars": 3360,
"preview": "// TODO: Replace `todo!()`s with the correct implementation.\n// Implement `IntoIterator` for `&TicketStore`. The iterat"
},
{
"path": "exercises/07_threads/00_intro/Cargo.toml",
"chars": 63,
"preview": "[package]\nname = \"intro_07\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/07_threads/00_intro/src/lib.rs",
"chars": 290,
"preview": "fn intro() -> &'static str {\n // TODO: fix me 👇\n \"I'm ready to _!\"\n}\n\n#[cfg(test)]\nmod tests {\n use crate::intr"
},
{
"path": "exercises/07_threads/01_threads/Cargo.toml",
"chars": 62,
"preview": "[package]\nname = \"threads\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/07_threads/01_threads/src/lib.rs",
"chars": 1218,
"preview": "// TODO: implement a multi-threaded version of the `sum` function\n// using `spawn` and `join`.\n// Given a vector of in"
},
{
"path": "exercises/07_threads/02_static/Cargo.toml",
"chars": 61,
"preview": "[package]\nname = \"static\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/07_threads/02_static/src/lib.rs",
"chars": 894,
"preview": "// TODO: Given a static slice of integers, split the slice into two halves and\n// sum each half in a separate thread.\n/"
},
{
"path": "exercises/07_threads/03_leak/Cargo.toml",
"chars": 62,
"preview": "[package]\nname = \"leaking\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/07_threads/03_leak/src/lib.rs",
"chars": 735,
"preview": "// TODO: Given a vector of integers, leak its heap allocation.\n// Then split the resulting static slice into two halves"
},
{
"path": "exercises/07_threads/04_scoped_threads/Cargo.toml",
"chars": 69,
"preview": "[package]\nname = \"scoped_threads\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/07_threads/04_scoped_threads/src/lib.rs",
"chars": 700,
"preview": "// TODO: Given a vector of integers, split it in two halves\n// and compute the sum of each half in a separate thread.\n/"
},
{
"path": "exercises/07_threads/05_channels/Cargo.toml",
"chars": 139,
"preview": "[package]\nname = \"channels\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helper"
},
{
"path": "exercises/07_threads/05_channels/src/data.rs",
"chars": 490,
"preview": "use crate::store::TicketId;\nuse ticket_fields::{TicketDescription, TicketTitle};\n\n#[derive(Clone, Debug, PartialEq)]\npub"
},
{
"path": "exercises/07_threads/05_channels/src/lib.rs",
"chars": 660,
"preview": "use std::sync::mpsc::{Receiver, Sender};\n\npub mod data;\npub mod store;\n\npub enum Command {\n Insert(todo!()),\n}\n\n// St"
},
{
"path": "exercises/07_threads/05_channels/src/store.rs",
"chars": 779,
"preview": "use crate::data::{Status, Ticket, TicketDraft};\nuse std::collections::BTreeMap;\n\n#[derive(Clone, Copy, Debug, PartialEq,"
},
{
"path": "exercises/07_threads/05_channels/tests/insert.rs",
"chars": 1102,
"preview": "// TODO: Set `move_forward` to `true` in `ready` when you think you're done with this exercise.\n// Feel free to call an"
},
{
"path": "exercises/07_threads/06_interior_mutability/Cargo.toml",
"chars": 74,
"preview": "[package]\nname = \"interior_mutability\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
},
{
"path": "exercises/07_threads/06_interior_mutability/src/lib.rs",
"chars": 1007,
"preview": "// TODO: Use `Rc` and `RefCell` to implement `DropTracker<T>`, a wrapper around a value of type `T`\n// that increments "
},
{
"path": "exercises/07_threads/07_ack/Cargo.toml",
"chars": 139,
"preview": "[package]\nname = \"response\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helper"
},
{
"path": "exercises/07_threads/07_ack/src/data.rs",
"chars": 490,
"preview": "use crate::store::TicketId;\nuse ticket_fields::{TicketDescription, TicketTitle};\n\n#[derive(Clone, Debug, PartialEq)]\npub"
},
{
"path": "exercises/07_threads/07_ack/src/lib.rs",
"chars": 942,
"preview": "use std::sync::mpsc::{Receiver, Sender};\nuse crate::store::TicketStore;\n\npub mod data;\npub mod store;\n\n// Refer to the t"
},
{
"path": "exercises/07_threads/07_ack/src/store.rs",
"chars": 873,
"preview": "use crate::data::{Status, Ticket, TicketDraft};\nuse std::collections::BTreeMap;\n\n#[derive(Clone, Copy, Debug, PartialEq,"
},
{
"path": "exercises/07_threads/07_ack/tests/insert.rs",
"chars": 1412,
"preview": "use response::data::{Status, Ticket, TicketDraft};\nuse response::store::TicketId;\nuse response::{launch, Command};\nuse t"
},
{
"path": "exercises/07_threads/08_client/Cargo.toml",
"chars": 137,
"preview": "[package]\nname = \"client\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helpers/"
},
{
"path": "exercises/07_threads/08_client/src/data.rs",
"chars": 490,
"preview": "use crate::store::TicketId;\nuse ticket_fields::{TicketDescription, TicketTitle};\n\n#[derive(Clone, Debug, PartialEq)]\npub"
},
{
"path": "exercises/07_threads/08_client/src/lib.rs",
"chars": 1697,
"preview": "use crate::data::{Ticket, TicketDraft};\nuse crate::store::{TicketId, TicketStore};\nuse std::sync::mpsc::{Receiver, Sende"
},
{
"path": "exercises/07_threads/08_client/src/store.rs",
"chars": 873,
"preview": "use crate::data::{Status, Ticket, TicketDraft};\nuse std::collections::BTreeMap;\n\n#[derive(Clone, Copy, Debug, PartialEq,"
},
{
"path": "exercises/07_threads/08_client/tests/insert.rs",
"chars": 707,
"preview": "use client::data::{Status, TicketDraft};\nuse client::launch;\nuse ticket_fields::test_helpers::{ticket_description, ticke"
},
{
"path": "exercises/07_threads/09_bounded/Cargo.toml",
"chars": 138,
"preview": "[package]\nname = \"bounded\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nticket_fields = { path = \"../../../helpers"
},
{
"path": "exercises/07_threads/09_bounded/src/data.rs",
"chars": 490,
"preview": "use crate::store::TicketId;\nuse ticket_fields::{TicketDescription, TicketTitle};\n\n#[derive(Clone, Debug, PartialEq)]\npub"
},
{
"path": "exercises/07_threads/09_bounded/src/lib.rs",
"chars": 1522,
"preview": "// TODO: Convert the implementation to use bounded channels.\nuse crate::data::{Ticket, TicketDraft};\nuse crate::store::{"
},
{
"path": "exercises/07_threads/09_bounded/src/store.rs",
"chars": 873,
"preview": "use crate::data::{Status, Ticket, TicketDraft};\nuse std::collections::BTreeMap;\n\n#[derive(Clone, Copy, Debug, PartialEq,"
},
{
"path": "exercises/07_threads/09_bounded/tests/insert.rs",
"chars": 629,
"preview": "use bounded::data::{Status, TicketDraft};\nuse bounded::launch;\nuse ticket_fields::test_helpers::{ticket_description, tic"
},
{
"path": "exercises/07_threads/10_patch/Cargo.toml",
"chars": 157,
"preview": "[package]\nname = \"patch\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nthiserror = \"1.0.69\"\nticket_fields = { path "
},
{
"path": "exercises/07_threads/10_patch/src/data.rs",
"chars": 695,
"preview": "use crate::store::TicketId;\nuse ticket_fields::{TicketDescription, TicketTitle};\n\n#[derive(Clone, Debug, PartialEq)]\npub"
},
{
"path": "exercises/07_threads/10_patch/src/lib.rs",
"chars": 2729,
"preview": "use std::sync::mpsc::{sync_channel, Receiver, SyncSender};\n\n// TODO: Implement the patching functionality.\nuse crate::da"
},
{
"path": "exercises/07_threads/10_patch/src/store.rs",
"chars": 983,
"preview": "use crate::data::{Status, Ticket, TicketDraft};\nuse std::collections::BTreeMap;\n\n#[derive(Clone, Copy, Debug, PartialEq,"
},
{
"path": "exercises/07_threads/10_patch/tests/check.rs",
"chars": 937,
"preview": "use patch::data::{Status, TicketDraft, TicketPatch};\nuse patch::launch;\nuse ticket_fields::test_helpers::{ticket_descrip"
},
{
"path": "exercises/07_threads/11_locks/Cargo.toml",
"chars": 157,
"preview": "[package]\nname = \"locks\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nthiserror = \"1.0.69\"\nticket_fields = { path "
},
{
"path": "exercises/07_threads/11_locks/src/data.rs",
"chars": 490,
"preview": "use crate::store::TicketId;\nuse ticket_fields::{TicketDescription, TicketTitle};\n\n#[derive(Clone, Debug, PartialEq)]\npub"
},
{
"path": "exercises/07_threads/11_locks/src/lib.rs",
"chars": 2623,
"preview": "// TODO: Fill in the missing methods for `TicketStore`.\n// Notice how we no longer need a separate update command: `Get"
}
]
// ... and 47 more files (download for full content)
About this extraction
This page contains the full source code of the mainmatter/100-exercises-to-learn-rust GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 247 files (205.6 KB), approximately 62.0k tokens, and a symbol index with 603 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.