├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── example ├── cafeteria │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── hello │ ├── Cargo.toml │ ├── benches │ │ └── snuggle_speed.rs │ ├── src │ │ ├── lib.rs │ │ └── main.rs │ └── tests │ │ └── anything.rs ├── kitchen │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── puzzle_game │ ├── .gitignore │ ├── Cargo.toml │ ├── puzzle.dat │ └── src │ │ └── main.rs └── puzzles │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── lib.rs └── exercise ├── closures_iterators ├── Cargo.toml └── src │ └── main.rs ├── docs ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── errors ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── idiomatic ├── Cargo.toml └── src │ └── main.rs ├── logging ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── testing ├── Cargo.toml └── src │ └── lib.rs ├── threads_channels ├── Cargo.toml └── src │ └── main.rs └── traits ├── Cargo.toml └── src └── main.rs /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/rust/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Debian OS version (use bullseye on local arm64/Apple Silicon): buster, bullseye 4 | ARG VARIANT="buster" 5 | FROM mcr.microsoft.com/vscode/devcontainers/rust:0-${VARIANT} 6 | 7 | # [Optional] Uncomment this section to install additional packages. 8 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | # && apt-get -y install --no-install-recommends 10 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/rust 3 | { 4 | "name": "Rust", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | // Use the VARIANT arg to pick a Debian OS version: buster, bullseye 9 | // Use bullseye when on local on arm64/Apple Silicon. 10 | "VARIANT": "bullseye" 11 | } 12 | }, 13 | "runArgs": [ 14 | "--cap-add=SYS_PTRACE", 15 | "--security-opt", 16 | "seccomp=unconfined" 17 | ], 18 | 19 | // Set *default* container specific settings.json values on container create. 20 | "settings": { 21 | "lldb.executable": "/usr/bin/lldb", 22 | // VS Code don't watch files under ./target 23 | "files.watcherExclude": { 24 | "**/target/**": true 25 | }, 26 | "rust-analyzer.checkOnSave.command": "clippy" 27 | }, 28 | 29 | // Add the IDs of extensions you want installed when the container is created. 30 | "extensions": [ 31 | "vadimcn.vscode-lldb", 32 | "mutantdino.resourcemonitor", 33 | "matklad.rust-analyzer", 34 | "tamasfe.even-better-toml", 35 | "serayuzgur.crates" 36 | ], 37 | 38 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 39 | // "forwardPorts": [], 40 | 41 | // Use 'postCreateCommand' to run commands after the container is created. 42 | // "postCreateCommand": "rustc --version", 43 | 44 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 45 | "remoteUser": "vscode", 46 | "features": { 47 | "git": "os-provided", 48 | "github-cli": "latest" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Generated by Cargo 3 | # will have compiled files and executables 4 | **/target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | **/Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # Generated by VS Code 14 | **/.vscode 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ "example/*", "exercise/*" ] 3 | resolver = "2" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nathan Stocks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ultimate Rust 2: Intermediate Concepts 2 | 3 | This is the companion repository for the `Ultimate Rust 2: Intermediate Concepts` (the followup to the popular [Ultimate Rust Crash Course]). _UR2IC_ will be published independently online in the second half of 2021 and is also presented live as part of some O'Reilly virtual events such as [Rust in 3 Weeks], or taught in-person for corporate training. You will get the most out of this training experience by doing the [exercises] in this repository and watching (or attending) the instructor-led training. 4 | 5 | In other words, this repository is for you hands-on-learners! 6 | 7 | I use macOS, and that is what I developed this course on. Everything _ought_ to work similarly on major Linux distributions and Windows. Please [contact me](mailto:nathan@agileperception.com) ASAP if you have trouble with anything on this page. 8 | 9 | _Just getting started with Rust? Check out the prerequisite for this course: [Ultimate Rust Crash Course]_ 10 | 11 | ## Install Rust & Prepare Your Development Environment 12 | 13 | Rust is required for this course! The latest stable version is always recommended. See [the repository for the previous course] for instructions on how to install Rust, prepare your development environment, and helpful resources. 14 | 15 | ## Exercises 16 | 17 | Please clone this repository! These exercises are designed as Rust projects for you to edit on your own computer. 18 | 19 | The exercises are separate Rust projects inside the `exercises/` subdirectory. For each exercise, you should: 20 | - Open the corresponding`exercise/EXERCISE_NAME` directory in your IDE/Editor 21 | - Navigate to the same directory with your Terminal application (so you can run `cargo run`, etc.) 22 | - Open up the `src/main.rs` file. 23 | - Follow the numbered exercise instructions in the code comments. 24 | 25 | If you encounter any problems with the exercises, please feel free to use the online course communication tools to contact me, or [open an discussion]. Either way. 😄 26 | 27 | For your convenience, here is a list of all the exercises, with links to view the code on GitHub. 28 | 29 | - [Exercise - Idiomatic Rust](https://github.com/CleanCut/ultimate_rust2/tree/main/exercise/idiomatic) 30 | - [Exercise - Documentation](https://github.com/CleanCut/ultimate_rust2/tree/main/exercise/docs) 31 | - [Exercise - Closures & Iterators](https://github.com/CleanCut/ultimate_rust2/tree/main/exercise/closures_iterators) 32 | - [Exercise - Common Traits](https://github.com/CleanCut/ultimate_rust2/tree/main/exercise/traits) 33 | - [Exercise - Errors](https://github.com/CleanCut/ultimate_rust2/tree/main/exercise/errors) - See also Jane Lusby's 2020 RustConf Session, [Error Handling Isn't All About Errors] 34 | - [Exercise - Testing](https://github.com/CleanCut/ultimate_rust2/tree/main/exercise/testing) 35 | - [Exercise - Logging](https://github.com/CleanCut/ultimate_rust2/tree/main/exercise/logging) 36 | - [Exercise - Threads & Channels](https://github.com/CleanCut/ultimate_rust2/tree/main/exercise/threads_channels) 37 | 38 | ## Examples 39 | 40 | This course goes over a lot of code in lecture format. Much of the code from the lectures can be found in the [`example/`] directory in this repository. 41 | 42 | ## Contribution 43 | 44 | All contributions are assumed to be dual-licensed under MIT/Apache-2. 45 | 46 | ## License 47 | 48 | Distributed under the terms of both the MIT license and the Apache License (Version 2.0). 49 | 50 | See [license/APACHE](license/APACHE) and [license/MIT](license/MIT). 51 | 52 | ## Sponsor 53 | 54 | If you like the work I do, please consider sponsoring me [on GitHub] or [on Patreon]. 💖 55 | 56 | [exercises]: https://github.com/CleanCut/ultimate_rust2#exercises 57 | [`example/`]: https://github.com/CleanCut/ultimate_rust2/blob/main/example 58 | [open an discussion]: https://github.com/CleanCut/ultimate_rust2/discussions/new 59 | [Ultimate Rust Crash Course]: https://agileperception.com/ultimate_rust_crash_course 60 | [Rust in 3 Weeks]: https://agileperception.com 61 | [Ultimate Rust 2: Intermediate Concepts]: https://github.com/CleanCut/ultimate_rust2 62 | [the repository for the previous course]: https://github.com/CleanCut/ultimate_rust_crash_course 63 | [on GitHub]: https://github.com/sponsors/CleanCut 64 | [on Patreon]: https://patreon.com/nathanstocks 65 | [Error Handling Isn't All About Errors]: https://www.youtube.com/watch?v=rAF8mLI0naQ 66 | -------------------------------------------------------------------------------- /example/cafeteria/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /example/cafeteria/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cafeteria" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | crossbeam = "0.8" 8 | -------------------------------------------------------------------------------- /example/cafeteria/src/main.rs: -------------------------------------------------------------------------------- 1 | use crossbeam::channel::{self, Receiver, Sender}; 2 | use std::{thread, time::Duration}; 3 | 4 | #[derive(Debug)] 5 | enum Lunch { 6 | Soup, 7 | Salad, 8 | Sandwich, 9 | HotDog, 10 | } 11 | 12 | fn cafeteria_worker(name: &str, orders: Receiver<&str>, lunches: Sender) { 13 | for order in orders { 14 | println!("{} receives an order for {}", name, order); 15 | let lunch = match &order { 16 | x if x.contains("soup") => Lunch::Soup, 17 | x if x.contains("salad") => Lunch::Salad, 18 | x if x.contains("sandwich") => Lunch::Sandwich, 19 | _ => Lunch::HotDog, 20 | }; 21 | for _ in 0..order.len() { 22 | thread::sleep(Duration::from_secs_f32(0.1)) 23 | } 24 | println!("{} sends a {:?}", name, lunch); 25 | if lunches.send(lunch).is_err() { 26 | break; 27 | } 28 | } 29 | } 30 | 31 | fn main() { 32 | let (orders_tx, orders_rx) = channel::unbounded(); 33 | let orders_rx2 = orders_rx.clone(); 34 | let (lunches_tx, lunches_rx) = channel::unbounded(); 35 | let lunches_tx2 = lunches_tx.clone(); 36 | 37 | let alice_handle = thread::spawn(|| cafeteria_worker("alice", orders_rx2, lunches_tx2)); 38 | let zack_handle = thread::spawn(|| cafeteria_worker("zack", orders_rx, lunches_tx)); 39 | 40 | for order in vec![ 41 | "polish dog", 42 | "caesar salad", 43 | "onion soup", 44 | "reuben sandwich", 45 | ] { 46 | println!("ORDER: {}", order); 47 | let _ = orders_tx.send(order); 48 | } 49 | drop(orders_tx); 50 | 51 | for lunch in lunches_rx { 52 | println!("Order Up! -> {:?}", lunch); 53 | } 54 | 55 | let _ = alice_handle.join(); 56 | let _ = zack_handle.join(); 57 | } 58 | -------------------------------------------------------------------------------- /example/hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | [dev-dependencies] 9 | criterion = { version = "0.3", features = ["html_reports"] } 10 | 11 | [[bench]] 12 | name = "snuggle_speed" 13 | harness = false 14 | -------------------------------------------------------------------------------- /example/hello/benches/snuggle_speed.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use hello::snuggle; 3 | 4 | pub fn snuggle_benchmark(c: &mut Criterion) { 5 | c.bench_function("snuggle 2", |b| b.iter(|| snuggle(black_box(2)))); 6 | } 7 | 8 | criterion_group!(benches, snuggle_benchmark); 9 | criterion_main!(benches); 10 | -------------------------------------------------------------------------------- /example/hello/src/lib.rs: -------------------------------------------------------------------------------- 1 | /// # Example 2 | /// 3 | /// ``` 4 | /// # use hello::snuggle; 5 | /// let bunnies = snuggle(5); 6 | /// assert_eq!(bunnies, 40); 7 | /// ``` 8 | pub fn snuggle(bunnies: u128) -> u128 { 9 | bunnies << 3 10 | } 11 | 12 | // The typical, multiplication approach 13 | // 14 | // pub fn snuggle(bunnies: u128) -> u128 { 15 | // bunnies * 8 16 | // } 17 | 18 | // The loop approach 19 | // 20 | // pub fn snuggle(bunnies: u128) -> u128 { 21 | // let mut result = 0; 22 | // for _ in 0..8 { 23 | // result += bunnies 24 | // } 25 | // result 26 | // } 27 | 28 | #[cfg(test)] 29 | mod test { 30 | use std::num::ParseIntError; 31 | 32 | use super::*; 33 | 34 | #[test] 35 | fn snuggling_bunnies_multiply() { 36 | assert_eq!(snuggle(2), 16); 37 | } 38 | 39 | #[should_panic] 40 | #[test] 41 | fn scared_bunny() { 42 | panic!("Hop hoppity hop!"); 43 | } 44 | 45 | #[test] 46 | fn bunny_result() -> Result<(), ParseIntError> { 47 | let num_bunnies: u64 = "4".parse()?; 48 | assert_eq!(num_bunnies, 4); 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /example/hello/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod test { 5 | 6 | #[test] 7 | fn the_bin_test() { 8 | assert_eq!(1 + 1, 2); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/hello/tests/anything.rs: -------------------------------------------------------------------------------- 1 | use hello::snuggle; 2 | 3 | #[test] 4 | fn it_works_from_outside() { 5 | assert!(snuggle(4) == 32); 6 | } 7 | -------------------------------------------------------------------------------- /example/kitchen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kitchen" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | log = "0.4" 8 | env_logger = "0.9" 9 | -------------------------------------------------------------------------------- /example/kitchen/src/main.rs: -------------------------------------------------------------------------------- 1 | use log::{error, info}; 2 | use std::{thread, time::Duration}; 3 | 4 | fn sleep(seconds: f32) { 5 | thread::sleep(Duration::from_secs_f32(seconds)); 6 | } 7 | 8 | pub mod dad { 9 | use super::{info, sleep}; 10 | 11 | pub fn cook_spaghetti() -> bool { 12 | info!("Cooking the spaghetti..."); 13 | sleep(4.0); 14 | info!("Spaghetti is ready!"); 15 | true 16 | } 17 | } 18 | 19 | pub mod mom { 20 | use super::{info, sleep}; 21 | 22 | pub fn cook_sauce_and_set_table() { 23 | sleep(1.0); 24 | info!("Cooking the sauce..."); 25 | sleep(2.0); 26 | info!("Sauce is ready! Setting the table..."); 27 | sleep(2.0); 28 | info!("Table is set!"); 29 | } 30 | } 31 | 32 | fn main() { 33 | env_logger::init(); 34 | let handle = thread::spawn(|| dad::cook_spaghetti()); 35 | 36 | mom::cook_sauce_and_set_table(); 37 | 38 | if handle.join().unwrap_or(false) { 39 | info!("Spaghetti time! Yum!") 40 | } else { 41 | error!("Dad messed up the spaghetti. Order pizza instead?"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /example/puzzle_game/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /example/puzzle_game/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "puzzle_game" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0" 8 | puzzles = { path = "../puzzles" } 9 | log = "0.4" 10 | env_logger = "0.9" 11 | -------------------------------------------------------------------------------- /example/puzzle_game/puzzle.dat: -------------------------------------------------------------------------------- 1 | this is a puzzle 2 | -------------------------------------------------------------------------------- /example/puzzle_game/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use log::info; 3 | use puzzles::Puzzle; 4 | use std::fs::File; 5 | 6 | fn get_puzzle(filename: &str) -> Result { 7 | let fh = File::open(filename) 8 | .with_context(|| format!("couldn't open the puzzle file {}", filename))?; 9 | let puzzle = Puzzle::from_file(fh).context("couldn't convert data into a puzzle")?; 10 | Ok(puzzle) 11 | } 12 | 13 | fn main() -> Result<()> { 14 | env_logger::init(); 15 | // This gets the absolute path to the puzzle.dat file in examples/puzzle_game no matter what 16 | // directory you are in when you run the `cargo run` command. 17 | let puzzle_file_path = &format!("{}/{}", env!("CARGO_MANIFEST_DIR"), "puzzle.dat"); 18 | let puzzle = match get_puzzle(puzzle_file_path).context("Couldn't get the first puzzle") { 19 | Ok(p) => p, 20 | Err(_) => Puzzle::new(), 21 | }; 22 | info!("Playing puzzle: {}", puzzle.name); 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /example/puzzles/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /example/puzzles/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "puzzles" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | thiserror = "1.0" 8 | log = "0.4" 9 | -------------------------------------------------------------------------------- /example/puzzles/src/lib.rs: -------------------------------------------------------------------------------- 1 | use log::{error, info}; 2 | use std::fs::File; 3 | use thiserror::Error; 4 | 5 | /// Number of pieces in the puzzle 6 | /// 7 | /// # History 8 | /// 9 | /// This is a separate paragraph. 10 | /// - Clickable link: [PUZZLE_PIECES] 11 | /// - We tried `7`, but this is better 12 | pub const PUZZLE_PIECES: u32 = 42; 13 | 14 | /// This is a Puzzle! 15 | #[derive(Clone, Debug)] 16 | pub struct Puzzle { 17 | /// Number of pieces 18 | pub num_pieces: u32, 19 | /// Descriptive name 20 | pub name: String, 21 | } 22 | 23 | impl Puzzle { 24 | /// Make a new puzzle! 25 | pub fn new() -> Self { 26 | let puzzle = Default::default(); 27 | info!("Created a puzzle with new(): {:?}", puzzle); 28 | puzzle 29 | } 30 | /// Load a puzzle from a file 31 | pub fn from_file(_fh: File) -> Result { 32 | println!("HERE"); 33 | error!("This file is missing a piece!"); 34 | Err(PuzzleError::MissingPiece) 35 | } 36 | } 37 | 38 | impl Default for Puzzle { 39 | fn default() -> Self { 40 | Puzzle { 41 | num_pieces: PUZZLE_PIECES, 42 | name: "Forest Lake".to_string(), 43 | } 44 | } 45 | } 46 | 47 | impl PartialEq for Puzzle { 48 | fn eq(self: &Puzzle, other: &Puzzle) -> bool { 49 | (self.num_pieces == other.num_pieces) 50 | && (self.name.to_lowercase() == other.name.to_lowercase()) 51 | } 52 | } 53 | 54 | impl From<&Puzzle> for String { 55 | fn from(puzzle: &Puzzle) -> Self { 56 | puzzle.name.clone() 57 | } 58 | } 59 | 60 | pub fn show>(s: T) { 61 | println!("{}", s.into()); 62 | } 63 | 64 | pub fn blah() { 65 | let puzzle = Puzzle::default(); 66 | show(&puzzle); 67 | // puzzle is still available! 68 | } 69 | 70 | #[derive(Clone, Copy)] 71 | pub enum PuzzleType { 72 | Jigsaw, 73 | } 74 | 75 | // struct Vehicle; 76 | // enum TransformerError { 77 | // Whatever, 78 | // } 79 | 80 | // struct Transformer; 81 | // impl Transformer { 82 | // pub fn new() -> Self { 83 | // Self {} 84 | // } 85 | // pub fn stand(self) -> Result { 86 | // Ok(self) 87 | // } 88 | // pub fn transform(self) -> Result { 89 | // Ok(self) 90 | // } 91 | // pub fn rollout(self) -> Result { 92 | // Ok(self) 93 | // } 94 | // pub fn chase(self) -> Result { 95 | // Ok(self) 96 | // } 97 | // } 98 | 99 | // pub fn autobots_rollout() -> Result { 100 | // let optimus = Transformer::new(); 101 | // try!(try!(try!(try!(optimus.stand()).transform()).rollout()).chase()) 102 | // // optimus.stand()?.transform()?.rollout()?.chase()? 103 | // } 104 | 105 | // optimus.stand()?.transform()?.rollout()?.chase()? 106 | 107 | // let stand = match optimus.stand() { 108 | // Ok(x) => x, 109 | // Err(e) => return Err(e), 110 | // }; 111 | // let transform = match stand.transform() { 112 | // Ok(x) => x, 113 | // Err(e) => return Err(e), 114 | // }; 115 | // let rollout = match transform.rollout() { 116 | // Ok(x) => x, 117 | // Err(e) => return Err(e), 118 | // }; 119 | // match transform.chase() { 120 | // Ok(x) => x, 121 | // Err(e) => return Err(e), 122 | // }; 123 | 124 | // match { 125 | // match { 126 | // match { 127 | // match optimus.stand() { 128 | // Ok(x) => x, 129 | // Err(e) => return Err(e), 130 | // } 131 | // } 132 | // .transform() 133 | // { 134 | // Ok(x) => x, 135 | // Err(e) => return Err(e), 136 | // } 137 | // } 138 | // .rollout() 139 | // { 140 | // Ok(x) => x, 141 | // Err(e) => return Err(e), 142 | // } 143 | // } 144 | // .chase() 145 | // { 146 | // Ok(x) => x, 147 | // Err(e) => return Err(e), 148 | // } 149 | // } 150 | 151 | // #[derive(Debug)] // #5: Debug + Display + Error 152 | // #[non_exhaustive] // #4: Non-Exhaustive 153 | // pub enum PuzzleError { 154 | // // #1: enum 155 | // WontFit(u16), // #2: Group Errors 156 | // MissingPiece, // #3: Only YOUR Errors 157 | // } 158 | 159 | // #5: Debug + Display + Error 160 | #[derive(Debug, Error)] 161 | #[non_exhaustive] 162 | pub enum PuzzleError { 163 | #[error("Piece {0} doesn't fit!")] 164 | WontFit(u16), 165 | #[error("Missing a piece")] 166 | MissingPiece, 167 | } 168 | 169 | // #5: Debug + Display + Error 170 | // impl Display for PuzzleError { 171 | // fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 172 | // use PuzzleError::*; 173 | // match self { 174 | // MissingPiece => write!(f, "Missing a piece"), 175 | // WontFit(n) => write!(f, "Piece {} doesn't fit!", n), 176 | // } 177 | // } 178 | // } 179 | 180 | // #5: Debug + Display + Error 181 | // impl Error for PuzzleError {} 182 | -------------------------------------------------------------------------------- /exercise/closures_iterators/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "closures_iterators" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exercise/closures_iterators/src/main.rs: -------------------------------------------------------------------------------- 1 | // Yes, yes, we know. It's an exercise, compiler, we want it that way! 2 | #[allow(unused_mut)] 3 | 4 | fn main() { 5 | // 1. Uncomment the code below. Create a closure that returns the square of an integer (the 6 | // number multiplied by itself), and assign the closure to the "square" variable. Then run the 7 | // code and make sure it works. 8 | 9 | // let square = ... 10 | // println!("5 squared is {}", square(5)); 11 | 12 | // 2. Uncomment the code below. Finish the .map() iterator adaptor call by passing it a closure 13 | // which takes a tuple of two integers as a parameter, and returns a tuple with the first 14 | // integer incremented by 1, and the second integer left alone. For example, if given the input 15 | // (0, 1), it should return (1, 1). Run the code and make sure it works. 16 | 17 | // let pairs = vec![(0, 1), (2, 3), (4, 5)]; 18 | // pairs 19 | // .into_iter() 20 | // .map( ... ) 21 | // .for_each(|t| println!("{:?}", t)); 22 | 23 | // 3. Uncomment the code below. There is a mutable vector named `numbers`. Use an iterator over 24 | // mutable references to multiply each of the values in `numbers` by 3. 25 | // Hint 1: You'll need .iter_mut() -- bonus points if you use the shorter, syntactic sugar form! 26 | // Hint 2: `x` will be a mutable reference, so remember to dereference it to use it 27 | 28 | // let mut numbers = vec![1, 2, 3, 4]; 29 | // for x in ... { 30 | // ... // multiply the value by 3 via the mutable reference x 31 | // } 32 | // println!("{:?}", numbers); // should print [3, 6, 9, 12] 33 | 34 | // 4. Uncomment the code below. Take the vector of words and 35 | // - Convert the vector into an iterator with .into_iter() 36 | // - Use .filter() to remove any word that contains the letter "h" -- use .contains() 37 | // - Use .map() to convert all the words to uppercase -- use .to_uppercase() 38 | // - Use .collect() to put the transformed words back into a vector 39 | // 40 | // Hint: .to_uppercase() is a method on `str` which returns a String 41 | 42 | // let words = vec!["autobot", "beach", "car", "decepticon", "energon", "frothy"]; 43 | // let transformed... // do the stuff here 44 | // println!("Transformed: {:?}", transformed); 45 | 46 | // Challenge: 47 | // 48 | // - Rewrite the code in #2 as a for loop 49 | // - Rewrite the code in #3 in functional style (without a for loop). Hint: There are multiple 50 | // ways to accomplish this, but they all end with an iterator consumer. 51 | } 52 | -------------------------------------------------------------------------------- /exercise/docs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "docs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exercise/docs/src/lib.rs: -------------------------------------------------------------------------------- 1 | // 1. Thank you for volunteering to document our pumpkin library! Let's start by grabbing the first 2 | // paragraph from https://en.wikipedia.org/wiki/Pumpkin and pasting it as our module-level 3 | // documentation. Hint: Use inner-documentation comments. 4 | // 5 | // Once you've got the documentation here, run `cargo doc --no-deps --open` and take a look! 6 | 7 | // 2. What about an image!? Add an image of a pumpkin to the end of the module-level documentation. 8 | // The markdown format is ![some alt text](https://url-to-the-image.png) 9 | // Here's the image to link to: https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/FrenchMarketPumpkinsB.jpg/700px-FrenchMarketPumpkinsB.jpg 10 | 11 | // 3. Document the Pumpkin struct. 12 | // - The description on the index page should be "Big orange thing" 13 | // - Make a section header called "Recipes" 14 | // - Explain that recipes will be coming soon. 15 | // - Document the "roundness" field, explaining that it is a percentage 16 | // - Document the "orangeness" field, explaining that it is a number from 8 to 27 17 | 18 | pub struct Pumpkin { 19 | pub roundness: f32, 20 | pub orangeness: i32, 21 | } 22 | 23 | // 4. Document the "smash" method. Explain that if you smash the pumpkin, it will be gone. Then it 24 | // can't be used for pie. :'-( 25 | 26 | impl Pumpkin { 27 | pub fn smash(self) {} 28 | } 29 | 30 | // 5. Document that BURNT_ORANGE is for the "orangeness" field in the Pumpkin struct. 31 | // - Link to the Pumpkin struct in your description 32 | 33 | pub const BURNT_ORANGE: i32 = 13; 34 | 35 | // Challenge: Find the option to pass to `cargo doc` so that documentation for this private item 36 | // gets generated as well. Hint: `cargo doc -h` will show you all the relevant options. 37 | 38 | /// For internal use only. In fact, this documentation is so private that it won't be generated. 39 | /// At least not by default. But if you pass the correct option in, it will magically appear! 40 | #[allow(dead_code)] // to silence the warning 41 | enum PrivateEnum { 42 | /// For Halloween. To be lit by candlelight. 43 | JackOLantern, 44 | /// For dessert during North American winter holidays. 45 | PumpkinPie, 46 | } 47 | -------------------------------------------------------------------------------- /exercise/docs/src/main.rs: -------------------------------------------------------------------------------- 1 | /// The exercise is in lib.rs this time!!! 2 | fn main() { 3 | println!("Go look in lib.rs ;-)"); 4 | } 5 | -------------------------------------------------------------------------------- /exercise/errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aquarium" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | # I thought you might want this for your library... 8 | thiserror = "1.0" 9 | # ...and this for your binary 10 | anyhow = "1.0" 11 | -------------------------------------------------------------------------------- /exercise/errors/src/lib.rs: -------------------------------------------------------------------------------- 1 | // 1. Create a DolphinError type representing the following three conditions: 2 | // - Hungry - The dolphin is hungry 3 | // - TooYoung - The dolphin is too young 4 | // - LongName - The dolphin's name is too long and annoying to say 5 | // 6 | // As a reminder, here are the 5 Guidelines for creating an error type: 7 | // (1) Use an `enum` for your error type 8 | // (2) Your error conditions should be enum variants grouped in as few enums as makes sense 9 | // (3) Don't expose error types other than your own (not going to be a problem for this exercise) 10 | // (4) Make your enum non-exhaustive 11 | // (5) Implement the Debug, Display, and Error traits 12 | // (5b) You can use thiserror's `Error` macro to derive the Display and Error traits 13 | // 14 | // Once you have completed defining the error type correctly, you should be able to run 15 | // `cargo build --lib` without any build errors or warnings. Then go to main.rs and continue with #2 16 | 17 | // pub enum DolphinError... 18 | 19 | pub struct Dolphin { 20 | pub name: String, 21 | pub age: u8, 22 | pub hungry: bool, 23 | } 24 | 25 | impl Dolphin { 26 | pub fn say_your_name(&self) -> Result { 27 | if self.name.len() > 10 { 28 | Err(DolphinError::LongName) 29 | } else { 30 | Ok(format!("Hi, my name is {} and I'm a Dolphin!", self.name)) 31 | } 32 | } 33 | pub fn flip(&self) -> Result { 34 | if self.age < 4 { 35 | Err(DolphinError::TooYoung) 36 | } else { 37 | Ok(format!("Yippee, I'm doing a flip!")) 38 | } 39 | } 40 | pub fn shake_hands(&self) -> Result { 41 | if self.hungry { 42 | Err(DolphinError::Hungry) 43 | } else { 44 | Ok(format!("Nice to meet you, let's shake hands!")) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /exercise/errors/src/main.rs: -------------------------------------------------------------------------------- 1 | // START IN lib.rs! 2 | 3 | use aquarium::Dolphin; 4 | // Silence some warnings so they don't distract from the exercise. 5 | #[allow(clippy::vec_init_then_push)] 6 | 7 | // (You already did #1 in lib.rs, right?) 8 | // 9 | // 2a. Uncomment and finish the play_time function below 10 | // - Bring anyhow::Result into scope with a `use` statement 11 | // - Have the play_time function return a `Result>`. The vector of Strings will 12 | // represent successful outcomes of various dolphin tricks. 13 | 14 | // fn play_time(dolphin: &Dolphin) -> ... { 15 | // let mut responses = vec![]; 16 | // // 2b. Call the .say_your_name() method on `dolphin`, use `?` to unwrap the value, and push 17 | // // the value onto the `responses` vector. 18 | // // 19 | // // let response = ... // this can be done with an intermediate variable... 20 | // // responses.push( ... ) // ...or all on one line. Either way is fine! 21 | // // 22 | // // 2c. Do the same thing as #2b for the .flip() method 23 | // // 24 | // // 2d. Do the same thing as #2b for the .shake_hands() method 25 | // 26 | // Ok(responses) 27 | // } 28 | 29 | fn main() { 30 | let dolphins = vec![ 31 | Dolphin { 32 | name: "Augustinius".into(), 33 | age: 7, 34 | hungry: false, 35 | }, 36 | Dolphin { 37 | name: "Bitty".into(), 38 | age: 2, 39 | hungry: true, 40 | }, 41 | Dolphin { 42 | name: "Carson".into(), 43 | age: 5, 44 | hungry: true, 45 | }, 46 | Dolphin { 47 | name: "Devin".into(), 48 | age: 6, 49 | hungry: false, 50 | }, 51 | ]; 52 | for dolphin in &dolphins { 53 | // Challenge: Change main() so that it returns a Result, and instead of handling the error 54 | // that play_time returns, use the try (?) operator to only handle the success condition. 55 | // 56 | // If done correctly, the output of the program will become much shorter. Since play_time 57 | // returns an Err variant the first time it is called, the try operator will return it from 58 | // main(), which will end the program at the first error. anyhow's Result will take care of 59 | // formatting the error output for us. 60 | match play_time(dolphin) { 61 | Ok(responses) => { 62 | println!("{} did a FABULOUS PERFORMANCE!", dolphin.name); 63 | for response in responses { 64 | println!(" {}", response); 65 | } 66 | } 67 | Err(e) => println!("{} can't perform today: {}", dolphin.name, e.to_string()), 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /exercise/idiomatic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "idiomatic" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exercise/idiomatic/src/main.rs: -------------------------------------------------------------------------------- 1 | // 1. This code looks terrible. Let's start cleaning this up by running `cargo fmt`. If you 2 | // configured your editor or IDE to run `cargo fmt` automatically upon save, you can just save! 3 | 4 | // 2. `cargo fmt` is great, but it doesn't add blank lines where there are none. Go ahead and add 5 | // some blank lines in places you think it would make sense. 6 | 7 | // 3. Time to clean up! Run `cargo clippy`. Fix up all the warnings so `cargo clippy` is silent. 8 | 9 | // Challenge: Clippy doesn't find *everything*. What else would you change to make this code better? 10 | 11 | const pi:f32=3.14159265358979323846; 12 | fn count_to_5()->i32{let mut foo =0;loop{if foo>pi as i32{if foo > 5{break;}}foo=foo+1;}return 5;} 13 | fn main() { 14 | println!("I can count to {}", count_to_5()); 15 | } 16 | #[cfg(test)] 17 | mod test { 18 | use super::*; 19 | #[test] 20 | fn test_counting() { 21 | assert_eq!(count_to_5() == 5, true); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /exercise/logging/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "frogger" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | # I'm glad you came to add the `log` dependency! I got it all ready for you, just uncomment: 8 | # 9 | # log = "0.4" 10 | 11 | # And here's the env_logger dependency that you'll need in main.rs 12 | # 13 | # env_logger = "0.9" 14 | -------------------------------------------------------------------------------- /exercise/logging/src/lib.rs: -------------------------------------------------------------------------------- 1 | // 1. Bring the macros `error, warn, info, debug, trace` into scope from the log package with a 2 | // `use` statement. 3 | // 4 | // You should be able to run `cargo build --lib` successfully after this step (and each step in this 5 | // file) 6 | // 7 | // Hint: You need to update Cargo.toml to add the `log` dependency, first. 8 | 9 | #[derive(Debug)] 10 | pub struct Frog { 11 | energy: u8, 12 | sleeping: bool, 13 | } 14 | 15 | impl Frog { 16 | pub fn new() -> Self { 17 | // 2. Use debug!() to log "A new Frog has been created" 18 | Default::default() 19 | } 20 | pub fn hop(&mut self) { 21 | self.energy -= 1; 22 | // 3. Use info!() to log that a Frog hopped, and how much energy is left 23 | if self.energy == 0 { 24 | // 4. Use warn!() to warn that the frog will go to sleep since he ran out of energy 25 | self.sleep(); 26 | } 27 | } 28 | pub fn sleep(&mut self) { 29 | if self.sleeping { 30 | // 5. Use error!() to log a (non-fatal) error stating that the Frog is already asleep 31 | } else { 32 | self.sleeping = true; 33 | } 34 | } 35 | } 36 | 37 | impl Default for Frog { 38 | fn default() -> Self { 39 | // 6. Use trace!() to log that a default value was generated, with the debug representation 40 | Frog { 41 | energy: 5, 42 | sleeping: false, 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /exercise/logging/src/main.rs: -------------------------------------------------------------------------------- 1 | // START IN lib.rs!!! 2 | 3 | use frogger::Frog; 4 | 5 | // You did #1-#6 in lib.rs already, right? 6 | // 7 | // 7. Update Cargo.toml to add the `env_logger` dependency 8 | 9 | fn main() { 10 | // 8. Initialize env_logger using the init() function at the top level of the library 11 | 12 | // 9. Run this program with `cargo run` and take a look at the default output. 13 | // - Now run it again with an explicit log level, like `RUST_LOG=info cargo run` 14 | // - or `RUST_LOG=trace cargo run` 15 | let mut skippy = Frog::new(); 16 | skippy.hop(); 17 | skippy.hop(); 18 | skippy.hop(); 19 | skippy.hop(); 20 | skippy.hop(); 21 | skippy.sleep(); 22 | skippy.sleep(); 23 | 24 | // Challenge: Go back to lib.rs and set the `target: ` argument for each logging call to be the 25 | // path to the function. For example, `Frog::new` 26 | } 27 | -------------------------------------------------------------------------------- /exercise/testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | 8 | 9 | # Challenge Help 1: If you choose to take on the challenge, you'll need to add `criterion` as a 10 | # development dependency. Here is one way to do it: 11 | 12 | # [dev-dependencies] 13 | # criterion = { version = "0.3", features = ["html_reports"] } 14 | 15 | # Challenge Help 2: Each benchmark needs a `[[bench]]` section with a name and disabling the harness. 16 | # A name "somename" will correspond with a file "benches/somename.rs" 17 | 18 | # [[bench]] 19 | # name = "somename" 20 | # harness = false 21 | 22 | # Challenge Help 3: The Criterion documentation has a great tutorial for how to actually write your 23 | # benchmark. Don't skip the part about `black_box()`! 24 | # https://bheisler.github.io/criterion.rs/book/getting_started.html#step-2---add-benchmark 25 | -------------------------------------------------------------------------------- /exercise/testing/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn sploosh(x: i32, y: i32, z: i32) -> i32 { 2 | match (x, y, z) { 3 | (x, _, _) if x < 0 => 99, 4 | (1, 2, 3) => 4, 5 | (5, 6, 7) => 3, 6 | (x, y, z) => x + y - z, 7 | } 8 | } 9 | 10 | pub fn splish(a: i32, b: i32) -> i32 { 11 | -a + 3 * b 12 | } 13 | 14 | // 1. Use the `cfg` attribute to mark the `test` module below as a test module 15 | 16 | mod test { 17 | // 2. Bring all the library items into scope with a `use` statement 18 | // Hint: It's okay to use `*` here. 19 | 20 | // 3. Write a test function that verifies the following condition using the `assert_eq!` or 21 | // `assert_ne!` macros 22 | // - sploosh(1, 2, 3) returns 4 23 | // - sploosh(5, 6, 7) does not return 4 24 | // - If you pass sploosh a negative number for the first argument, 99 is returned 25 | // 26 | // `cargo test` should run your tests and pass 27 | // Hint: Don't forget the `#[test]` attribute for your test function! 28 | 29 | // 4. Write a test function that verifies the following conditions using the `assert!` macro 30 | // - splish(100, 10) is negative 31 | // - splish(40, 20) is positive 32 | // - splish(9, 3) is 0 33 | } 34 | 35 | // 5. Create a `tests/` directory and an integration test file `tests/more_tests.rs` 36 | // Inside that file, create a test function that verifies: 37 | // - that `sploosh(splish(-1, 0), splish(1, 1), splish(3, 2))` returns the value `4` 38 | // 39 | // `cargo test` should run your `more_tests.rs` file and pass 40 | 41 | // Challenge: Create a benchmark that measures the speed of sploosh(8, 9, 10) 42 | // - Speed up the implementation of sploosh(8, 9, 10) without breaking the other tests. 43 | // - Hint: See Cargo.toml to get you started 44 | -------------------------------------------------------------------------------- /exercise/threads_channels/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "threads_channels" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | crossbeam = "0.8" 8 | -------------------------------------------------------------------------------- /exercise/threads_channels/src/main.rs: -------------------------------------------------------------------------------- 1 | // Silence some warnings so they don't distract from the exercise. 2 | #![allow(dead_code, unused_imports, unused_variables)] 3 | use crossbeam::channel; 4 | use std::thread; 5 | use std::time::Duration; 6 | 7 | fn sleep_ms(ms: u64) { 8 | thread::sleep(Duration::from_millis(ms)); 9 | } 10 | 11 | fn expensive_sum(v: Vec) -> i32 { 12 | // Pretend our fancy little filter-map-sum is expensive and takes 500ms 13 | sleep_ms(500); 14 | println!("Child thread: just about finished"); 15 | v.iter().filter(|&x| x % 2 == 0).map(|x| x * x).sum() 16 | } 17 | 18 | fn main() { 19 | let my_vector = vec![2, 5, 1, 0, 4, 3]; 20 | 21 | // 1. Spawn a child thread and have it call `expensive_sum(my_vector)`. Store the returned 22 | // join handle in a variable called `handle`. Once you've done this you should be able to run 23 | // the code and see the output from the child thread's expensive sum in the middle of the main 24 | // thread's processing of letters. 25 | // 26 | //let handle = ... 27 | 28 | // While the child thread is running, the main thread will also do some work 29 | for letter in vec!["a", "b", "c", "d", "e", "f"] { 30 | println!("Main thread: Processing the letter '{}'", letter); 31 | sleep_ms(200); 32 | } 33 | 34 | // 2. Let's retrieve the value returned by the child thread once it has exited. 35 | // - Uncomment and complete the code below. 36 | // - Call the .join() method on `handle` from #1 and assign the `Result` it returns 37 | // to a variable named `result` 38 | // - Get the i32 out of `result` and store it in a `sum` variable. 39 | 40 | // let result = 41 | // let sum = 42 | // println!("The child thread's expensive sum is {}", sum); 43 | 44 | // 3. Time for some fun with channels! 45 | // - Uncomment the block comment below (Find and remove the `/*` and `*/`). 46 | // - Create variables `tx` and `rx` and assign them to the sending and receiving ends of an 47 | // unbounded channel. Hint: An unbounded channel can be created with `channel::unbounded()` 48 | 49 | /* 50 | // let ... 51 | 52 | // Cloning a channel makes another variable connected to that end of the channel so that you can 53 | // send it to another thread. We want another variable that can be used for sending... 54 | let tx2 = tx.clone(); 55 | 56 | // 4. Examine the flow of execution of "Thread A" and "Thread B" below. Do you see how their 57 | // output will mix with each other? 58 | // - Run this code. Notice the order of output from Thread A and Thread B. 59 | // - Increase the value passed to the first `sleep_ms()` call in Thread A so that both the 60 | // Thread B outputs occur *before* Thread A outputs anything. 61 | // - Run the code again and make sure the output comes in a different order. 62 | 63 | // Thread A 64 | let handle_a = thread::spawn(move || { 65 | sleep_ms(0); 66 | tx2.send("Thread A: 1").unwrap(); 67 | sleep_ms(200); 68 | tx2.send("Thread A: 2").unwrap(); 69 | }); 70 | 71 | sleep_ms(100); // Make sure Thread A has time to get going before we spawn Thread B 72 | 73 | // Thread B 74 | let handle_b = thread::spawn(move || { 75 | sleep_ms(0); 76 | tx.send("Thread B: 1").unwrap(); 77 | sleep_ms(200); 78 | tx.send("Thread B: 2").unwrap(); 79 | }); 80 | 81 | // Using a Receiver channel as an iterator is a convenient way to get values until the channel 82 | // gets closed. A Receiver channel is automatically closed once all Sender channels have been 83 | // closed. Both our threads automatically close their Sender channels when they exit and the 84 | // destructors for the channels get automatically called. 85 | for msg in rx { 86 | println!("Main thread: Received {}", msg); 87 | } 88 | 89 | // 5. Oops, we forgot to join "Thread A" and "Thread B". That's bad hygiene! 90 | // - Use the thread handles to join both threads without getting any compiler warnings. 91 | */ 92 | 93 | // Challenge: Make two child threads and give them each a receiving end to a channel. From the 94 | // main thread loop through several values and print each out and then send it to the channel. 95 | // On the child threads print out the values you receive. Close the sending side in the main 96 | // thread by calling `drop(tx)` (assuming you named your sender channel variable `tx`). Join 97 | // the child threads. 98 | println!("Main thread: Exiting.") 99 | } 100 | -------------------------------------------------------------------------------- /exercise/traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "traits" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /exercise/traits/src/main.rs: -------------------------------------------------------------------------------- 1 | pub enum Cake { 2 | Chocolate, 3 | MapleBacon, 4 | Spice, 5 | } 6 | 7 | pub struct Party { 8 | pub at_restaurant: bool, 9 | pub num_people: u8, 10 | pub cake: Cake, 11 | } 12 | 13 | fn main() { 14 | // 1. The code below doesn't work because Cake doesn't implement Debug. 15 | // - Derive the Debug trait for the Cake enum above so this code will work. Then, run the code. 16 | 17 | let cake = Cake::Spice; 18 | admire_cake(cake); 19 | 20 | // 2. Uncomment the code below. It doesn't work since `cake` was *moved* into the admire_cake() 21 | // function. Let's fix the Cake enum so the code below works without any changes. 22 | // - Derive the Copy trait for the Cake enum so that `cake` gets copied into the admire_cake() 23 | // function instead of moved. 24 | // - Hint: You may need to derive another trait in order to be able to derive the Copy trait 25 | 26 | // match cake { 27 | // Cake::Chocolate => println!("The name's Chocolate. Dark...Chocolate."), 28 | // Cake::MapleBacon => println!("Dreams do come true!"), 29 | // Cake::Spice => println!("Great, let's spice it up!"), 30 | // } 31 | 32 | // 3. Uncomment the println below. It doesn't work since the Party struct doesn't implement the 33 | // Debug or Default traits. 34 | // - Derive the Debug trait for the Party struct 35 | // - Manually implement the Default trait for the Party struct. Use the value below as the 36 | // default value that you return from the `default` method: 37 | // 38 | // Party { 39 | // at_restaurant: true, 40 | // num_people: 8, 41 | // cake: Cake::Chocolate, 42 | // } 43 | // 44 | // Hint: If you get stuck, there is an example at 45 | // https://doc.rust-lang.org/std/default/trait.Default.html#how-can-i-implement-default 46 | 47 | // println!("The default Party is\n{:#?}", Party::default()); 48 | 49 | // 4. You prefer Maple Bacon cake. Use "struct update syntax" to create a Party with `cake` 50 | // set to `Cake::MapleBacon`, but the rest of the values are default. 51 | // 52 | // Hint: The trick to struct update syntax is specifying the value(s) you want to customize 53 | // first and then ending the struct with `..Default::default()` -- but no comma after that! 54 | 55 | // let party = Party { 56 | // ... 57 | // }; 58 | // println!("Yes! My party has my favorite {:?} cake!", party.cake); 59 | 60 | // 5. Parties are "equal" if they have the same cake. 61 | // - Derive the PartialEq trait for the Cake enum so Cakes can be compared. 62 | // - Manually implement the PartialEq trait for Party. If different parties have the same cake, 63 | // then they are equal, no matter the location or number of attendees at the party. 64 | // - Uncomment and run the code below. 65 | 66 | // let other_party = Party { 67 | // at_restaurant: false, 68 | // num_people: 235, 69 | // cake: Cake::MapleBacon, 70 | // }; 71 | // if party == other_party { 72 | // println!("Your party is just like mine!"); 73 | // } 74 | 75 | // Challenge: You would like to be able to pass a Party struct into the smell_cake() function 76 | // which takes a type T which implements the Into trait. 77 | // - Uncomment the code below AND uncomment the smell_cake() function at the bottom of this file 78 | // - Implement `From for Cake` so that the function call below works. 79 | // 80 | 81 | // smell_cake(party); 82 | 83 | // Challenge 2: Implement `From<&Party> for Cake` so that you can smell your cake without 84 | // consuming it. Change the code above to pass in a &party. Then uncomment and run the code 85 | // below. After all, you want to smell your cake and eat it, too! 86 | 87 | // println!("Yum! I'm eating this cake: {:?}. Oops, I dropped it on the floor.", party.cake); 88 | // drop(cake); 89 | } 90 | 91 | pub fn admire_cake(cake: Cake) { 92 | println!("What a nice {:?} cake! 🎂", cake); 93 | } 94 | 95 | // pub fn smell_cake>(something: T) { 96 | // println!("Hmm...something smells like a {:?} cake!", something.into()); 97 | // } 98 | --------------------------------------------------------------------------------