Compare commits

...

18 Commits

Author SHA1 Message Date
47ed183cfe cargo fmt 2026-05-26 14:42:05 +04:00
76cb232b1e Complete exercise: threads_channels 2026-05-26 14:38:45 +04:00
621fb6bb59 Complete exercise: logging 2026-05-26 00:08:56 +04:00
d745026c1a Complete exercise: testing 2026-05-25 16:27:58 +04:00
b25fe11bc7 Complete exercise: errors 2026-05-25 15:32:14 +04:00
89fd568b75 Complete exercise: traits 2026-05-25 15:07:20 +04:00
595ac0e75b Complete exercise: closures_iterators 2026-05-21 00:11:21 +04:00
c17cfe0169 Complete exercise: docs 2026-05-17 23:06:59 +04:00
7194a5d29e Complete exercise: idomatic 2026-05-17 22:45:35 +04:00
Nathan Stocks
5e852ca5d5 fix typo 2025-10-30 16:29:28 -06:00
Nathan Stocks
0b362a2a7b freshen up 2025-10-30 16:16:10 -06:00
Nathan Stocks
1b1ced37a4 just in case someone tries to use rusty_engine from within this repo 2023-02-10 20:24:11 -07:00
Magnus Markling
de71e01a95 even more 2022-09-19 14:11:25 -06:00
Magnus Markling
c100b83038 Remove more extra spaces 2022-09-19 14:11:25 -06:00
Magnus Markling
5c0dfcda33 Remove extra space from excercise 2022-09-19 14:11:25 -06:00
Jamil Lambert, PhD
ea93715738 Updated the comment to match the code
The code requires the student to create a Party not  a Dessert as originally stated in the comment
2022-09-14 10:31:47 -06:00
Korny666
036e7b7b50 // Silence some warnings so they don't distract from the exercise. clippy::vec_init_then_push is distracting here 2022-03-09 14:29:38 -07:00
Nathan Stocks
a47afbbb6e fix logic for logging step 5, output should now trigger two errors in a row 2022-02-20 14:20:49 -07:00
16 changed files with 285 additions and 130 deletions

View File

@@ -1,2 +1,3 @@
[workspace] [workspace]
members = [ "example/*", "exercise/*" ] members = [ "example/*", "exercise/*" ]
resolver = "2"

View File

@@ -1,6 +1,6 @@
# Ultimate Rust 2: Intermediate Concepts # Ultimate Rust 2: Intermediate Concepts
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. This is the companion repository for the `Ultimate Rust 2: Intermediate Concepts` (the followup to the popular [Ultimate Rust Crash Course]). 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.
In other words, this repository is for you hands-on-learners! In other words, this repository is for you hands-on-learners!
@@ -22,7 +22,7 @@ The exercises are separate Rust projects inside the `exercises/` subdirectory.
- Open up the `src/main.rs` file. - Open up the `src/main.rs` file.
- Follow the numbered exercise instructions in the code comments. - Follow the numbered exercise instructions in the code comments.
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. 😄 If you encounter any problems with the exercises, please feel free to use the online course communication tools to contact me, or [open a discussion]. Either way. 😄
For your convenience, here is a list of all the exercises, with links to view the code on GitHub. For your convenience, here is a list of all the exercises, with links to view the code on GitHub.
@@ -55,7 +55,7 @@ If you like the work I do, please consider sponsoring me [on GitHub] or [on Patr
[exercises]: https://github.com/CleanCut/ultimate_rust2#exercises [exercises]: https://github.com/CleanCut/ultimate_rust2#exercises
[`example/`]: https://github.com/CleanCut/ultimate_rust2/blob/main/example [`example/`]: https://github.com/CleanCut/ultimate_rust2/blob/main/example
[open an discussion]: https://github.com/CleanCut/ultimate_rust2/discussions/new [open a discussion]: https://github.com/CleanCut/ultimate_rust2/discussions/new
[Ultimate Rust Crash Course]: https://agileperception.com/ultimate_rust_crash_course [Ultimate Rust Crash Course]: https://agileperception.com/ultimate_rust_crash_course
[Rust in 3 Weeks]: https://agileperception.com [Rust in 3 Weeks]: https://agileperception.com
[Ultimate Rust 2: Intermediate Concepts]: https://github.com/CleanCut/ultimate_rust2 [Ultimate Rust 2: Intermediate Concepts]: https://github.com/CleanCut/ultimate_rust2

View File

@@ -6,30 +6,31 @@ fn main() {
// number multiplied by itself), and assign the closure to the "square" variable. Then run the // number multiplied by itself), and assign the closure to the "square" variable. Then run the
// code and make sure it works. // code and make sure it works.
// let square = ... let square = |x| x * x;
// println!("5 squared is {}", square(5)); println!("5 squared is {}", square(5));
// 2. Uncomment the code below. Finish the .map() iterator adaptor call by passing it a closure // 2. Uncomment the code below. Finish the .map() iterator adaptor call by passing it a closure
// which takes a tuple of two integers as a parameter, and returns a tuple with the first // which takes a tuple of two integers as a parameter, and returns a tuple with the first
// integer incremented by 1, and the second integer left alone. For example, if given the input // integer incremented by 1, and the second integer left alone. For example, if given the input
// (0, 1), it should return (1, 1). Run the code and make sure it works. // (0, 1), it should return (1, 1). Run the code and make sure it works.
// let pairs = vec![(0, 1), (2, 3), (4, 5)]; let pairs = vec![(0, 1), (2, 3), (4, 5)];
// pairs pairs
// .into_iter() .into_iter()
// .map( ... ) .map(|t| (t.0 + 1, t.1))
// .for_each(|t| println!("{:?}", t)); .for_each(|t| println!("{:?}", t));
// 3. Uncomment the code below. There is a mutable vector named `numbers`. Use an iterator over // 3. Uncomment the code below. There is a mutable vector named `numbers`. Use an iterator over
// mutable references to multiply each of the values in `numbers` by 3. // mutable references to multiply each of the values in `numbers` by 3.
// Hint 1: You'll need .iter_mut() -- bonus points if you use the shorter, syntactic sugar form! // Hint 1: You'll need .iter_mut() -- bonus points if you use the shorter, syntactic sugar form!
// Hint 2: `x` will be a mutable reference, so remember to dereference it to use it // Hint 2: `x` will be a mutable reference, so remember to dereference it to use it
// let mut numbers = vec![1, 2, 3, 4]; let mut numbers = vec![1, 2, 3, 4];
// for x in ... { // numbers = numbers.iter_mut().map(|x| *x * 3).collect();
// ... // multiply the value by 3 via the mutable reference x for n in &mut numbers {
// } *n = *n * 3;
// println!("{:?}", numbers); // should print [3, 6, 9, 12] }
println!("{:?}", numbers); // should print [3, 6, 9, 12]
// 4. Uncomment the code below. Take the vector of words and // 4. Uncomment the code below. Take the vector of words and
// - Convert the vector into an iterator with .into_iter() // - Convert the vector into an iterator with .into_iter()
@@ -39,9 +40,13 @@ fn main() {
// //
// Hint: .to_uppercase() is a method on `str` which returns a String // Hint: .to_uppercase() is a method on `str` which returns a String
// let words = vec!["autobot", "beach", "car", "decepticon", "energon", "frothy"]; let words = vec!["autobot", "beach", "car", "decepticon", "energon", "frothy"];
// let transformed... // do the stuff here let transformed = words
// println!("Transformed: {:?}", transformed); .into_iter()
.filter(|w| !w.contains("h"))
.map(|w| w.to_uppercase())
.collect::<Vec<String>>();
println!("Transformed: {:?}", transformed);
// Challenge: // Challenge:
// //

View File

@@ -4,10 +4,17 @@
// //
// Once you've got the documentation here, run `cargo doc --no-deps --open` and take a look! // Once you've got the documentation here, run `cargo doc --no-deps --open` and take a look!
//! A pumpkin is a cultivated winter squash in the genus Cucurbita.
//! The term is most commonly applied to round, orange-colored squash varieties,
//! but does not possess a scientific definition. It may be used in reference to
//! many different squashes of varied appearance and belonging to multiple species in the Cucurbita genus.
// 2. What about an image!? Add an image of a pumpkin to the end of the module-level documentation. // 2. What about an image!? Add an image of a pumpkin to the end of the module-level documentation.
// The markdown format is ![some alt text](https://url-to-the-image.png) // The markdown format is ![some alt text](https://url-to-the-image.png)
// Here's the image to link to: https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/FrenchMarketPumpkinsB.jpg/700px-FrenchMarketPumpkinsB.jpg // Here's the image to link to: https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/FrenchMarketPumpkinsB.jpg/700px-FrenchMarketPumpkinsB.jpg
//! ![Pumpkin Image](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/FrenchMarketPumpkinsB.jpg/700px-FrenchMarketPumpkinsB.jpg)
// 3. Document the Pumpkin struct. // 3. Document the Pumpkin struct.
// - The description on the index page should be "Big orange thing" // - The description on the index page should be "Big orange thing"
// - Make a section header called "Recipes" // - Make a section header called "Recipes"
@@ -15,8 +22,16 @@
// - Document the "roundness" field, explaining that it is a percentage // - Document the "roundness" field, explaining that it is a percentage
// - Document the "orangeness" field, explaining that it is a number from 8 to 27 // - Document the "orangeness" field, explaining that it is a number from 8 to 27
/// Big orange thing
///
/// # Recipes
///
/// Recipes will be coming soon.
pub struct Pumpkin { pub struct Pumpkin {
/// `roundness` is a percentage that describes how round the pumpkin is.
/// 100% means perfectly round, while 0% means not round at all.
pub roundness: f32, pub roundness: f32,
/// `orangeness` is a number from 8 to 27 that describes how orange the pumpkin is.
pub orangeness: i32, pub orangeness: i32,
} }
@@ -24,12 +39,14 @@ pub struct Pumpkin {
// can't be used for pie. :'-( // can't be used for pie. :'-(
impl Pumpkin { impl Pumpkin {
/// Smash the pumpkin. Once smashed, it cannot be used for pie.
pub fn smash(self) {} pub fn smash(self) {}
} }
// 5. Document that BURNT_ORANGE is for the "orangeness" field in the Pumpkin struct. // 5. Document that BURNT_ORANGE is for the "orangeness" field in the Pumpkin struct.
// - Link to the Pumpkin struct in your description // - Link to the Pumpkin struct in your description
/// The `BURNT_ORANGE` constant is a value of 13 that can be used for the `orangeness` field in the [Pumpkin] struct.
pub const BURNT_ORANGE: i32 = 13; pub const BURNT_ORANGE: i32 = 13;
// Challenge: Find the option to pass to `cargo doc` so that documentation for this private item // Challenge: Find the option to pass to `cargo doc` so that documentation for this private item

View File

@@ -14,7 +14,18 @@
// Once you have completed defining the error type correctly, you should be able to run // Once you have completed defining the error type correctly, you should be able to run
// `cargo build --lib` without any build errors or warnings. Then go to main.rs and continue with #2 // `cargo build --lib` without any build errors or warnings. Then go to main.rs and continue with #2
// pub enum DolphinError... use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DolphinError {
#[error("The dolphin is hungry")]
Hungry,
#[error("The dolphin is too young")]
TooYoung,
#[error("The dolphin's name is too long and annoying to say")]
LongName,
}
pub struct Dolphin { pub struct Dolphin {
pub name: String, pub name: String,

View File

@@ -1,30 +1,34 @@
// START IN lib.rs! // START IN lib.rs!
use aquarium::Dolphin; use aquarium::Dolphin;
// Silence some warnings so they don't distract from the exercise.
#[allow(clippy::vec_init_then_push)]
// (You already did #1 in lib.rs, right?) // (You already did #1 in lib.rs, right?)
// //
// 2a. Uncomment and finish the play_time function below // 2a. Uncomment and finish the play_time function below
// - Bring anyhow::Result into scope with a `use` statement // - Bring anyhow::Result into scope with a `use` statement
// - Have the play_time function return a `Result<Vec<String>>`. The vector of Strings will // - Have the play_time function return a `Result<Vec<String>>`. The vector of Strings will
// represent successful outcomes of various dolphin tricks. // represent successful outcomes of various dolphin tricks.
use anyhow::Result;
// fn play_time(dolphin: &Dolphin) -> ... { fn play_time(dolphin: &Dolphin) -> Result<Vec<String>> {
// let mut responses = vec![]; let mut responses = vec![];
// // 2b. Call the .say_your_name() method on `dolphin`, use `?` to unwrap the value, and push // 2b. Call the .say_your_name() method on `dolphin`, use `?` to unwrap the value, and push
// // the value onto the `responses` vector. // the value onto the `responses` vector.
// //
// // let response = ... // this can be done with an intermediate variable...
// // responses.push( ... ) // ...or all on one line. Either way is fine!
// //
// // 2c. Do the same thing as #2b for the .flip() method
// //
// // 2d. Do the same thing as #2b for the .shake_hands() method
// //
// Ok(responses) let response = dolphin.say_your_name()?; // this can be done with an intermediate variable...
// } responses.push(response); // ...or all on one line. Either way is fine!
//
// 2c. Do the same thing as #2b for the .flip() method
responses.push(dolphin.flip()?);
//
// 2d. Do the same thing as #2b for the .shake_hands() method
responses.push(dolphin.shake_hands()?);
fn main() { Ok(responses)
}
fn main() -> Result<()> {
let dolphins = vec![ let dolphins = vec![
Dolphin { Dolphin {
name: "Augustinius".into(), name: "Augustinius".into(),
@@ -55,14 +59,23 @@ fn main() {
// returns an Err variant the first time it is called, the try operator will return it from // returns an Err variant the first time it is called, the try operator will return it from
// main(), which will end the program at the first error. anyhow's Result will take care of // main(), which will end the program at the first error. anyhow's Result will take care of
// formatting the error output for us. // formatting the error output for us.
match play_time(dolphin) { match play_time(dolphin) {
Ok(responses) => { Ok(responses) => {
println!("{} did a FABULOUS PERFORMANCE!", dolphin.name); println!("\n{} did a FABULOUS PERFORMANCE!", dolphin.name);
for response in responses { for response in responses {
println!(" {}", response); println!(" {}", response);
} }
} }
Err(e) => println!("{} can't perform today: {}", dolphin.name, e.to_string()), Err(e) => println!("{} can't perform today: {}", dolphin.name, e.to_string()),
} }
// let responses = play_time(dolphin)?;
// println!("\n{} did a FABULOUS PERFORMANCE!", dolphin.name);
// for response in responses {
// println!(" {}", response);
// }
} }
Ok(())
} }

View File

@@ -8,14 +8,26 @@
// Challenge: Clippy doesn't find *everything*. What else would you change to make this code better? // Challenge: Clippy doesn't find *everything*. What else would you change to make this code better?
const pi:f32=3.14159265358979323846; use std::f32::consts::PI;
fn count_to_5()->i32{let mut foo =0;loop{if foo>pi as i32{if foo > 5{break;}}foo=foo+1;}return 5;}
fn count_to_5() -> i32 {
let mut count = 0;
loop {
if count > PI as i32 && count >= 5 {
break;
}
count += 1;
}
count
}
fn main() { fn main() {
println!("I can count to {}", count_to_5()); println!("I can count to {}", count_to_5());
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
#[test] #[test]
fn test_counting() { fn test_counting() {
assert_eq!(count_to_5() == 5, true); assert_eq!(count_to_5() == 5, true);

View File

@@ -4,10 +4,5 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
# I'm glad you came to add the `log` dependency! I got it all ready for you, just uncomment: log = "0.4" # I'm glad you came to add the `log` dependency! I got it all ready for you, just uncomment:
# env_logger = "0.9" # And here's the env_logger dependency that you'll need in main.rs
# log = "0.4"
# And here's the env_logger dependency that you'll need in main.rs
#
# env_logger = "0.9"

View File

@@ -6,6 +6,8 @@
// //
// Hint: You need to update Cargo.toml to add the `log` dependency, first. // Hint: You need to update Cargo.toml to add the `log` dependency, first.
use log::{debug, error, info, trace, warn};
#[derive(Debug)] #[derive(Debug)]
pub struct Frog { pub struct Frog {
energy: u8, energy: u8,
@@ -15,19 +17,24 @@ pub struct Frog {
impl Frog { impl Frog {
pub fn new() -> Self { pub fn new() -> Self {
// 2. Use debug!() to log "A new Frog has been created" // 2. Use debug!() to log "A new Frog has been created"
debug!(target: "Frog::new", "A new Frog has been created");
Default::default() Default::default()
} }
pub fn hop(&mut self) { pub fn hop(&mut self) {
self.energy -= 1; self.energy -= 1;
// 3. Use info!() to log that a Frog hopped, and how much energy is left // 3. Use info!() to log that a Frog hopped, and how much energy is left
info!(target: "Frog::hop", "A frog hopped! It has {} energy left", self.energy);
if self.energy == 0 { if self.energy == 0 {
// 4. Use warn!() to warn that the frog will go to sleep since he ran out of energy // 4. Use warn!() to warn that the frog will go to sleep since he ran out of energy
warn!(target: "Frog::hop", "The frog will go to sleep since he ran out of energy");
self.sleep(); self.sleep();
} }
} }
pub fn sleep(&mut self) { pub fn sleep(&mut self) {
if !self.sleeping { if self.sleeping {
// 5. Use error!() to log a (non-fatal) error stating that the Frog is already asleep // 5. Use error!() to log a (non-fatal) error stating that the Frog is already asleep
error!(target: "Frog::sleep", "The frog is already asleep");
} else {
self.sleeping = true; self.sleeping = true;
} }
} }
@@ -36,9 +43,12 @@ impl Frog {
impl Default for Frog { impl Default for Frog {
fn default() -> Self { fn default() -> Self {
// 6. Use trace!() to log that a default value was generated, with the debug representation // 6. Use trace!() to log that a default value was generated, with the debug representation
Frog { let frog = Frog {
energy: 5, energy: 5,
sleeping: false, sleeping: false,
} };
trace!(target: "Frog::Default", "A default Frog value was generated: {:?}", frog);
frog
} }
} }

View File

@@ -8,6 +8,7 @@ use frogger::Frog;
fn main() { fn main() {
// 8. Initialize env_logger using the init() function at the top level of the library // 8. Initialize env_logger using the init() function at the top level of the library
env_logger::init();
// 9. Run this program with `cargo run` and take a look at the default output. // 9. Run this program with `cargo run` and take a look at the default output.
// - Now run it again with an explicit log level, like `RUST_LOG=info cargo run` // - Now run it again with an explicit log level, like `RUST_LOG=info cargo run`
@@ -19,6 +20,7 @@ fn main() {
skippy.hop(); skippy.hop();
skippy.hop(); skippy.hop();
skippy.sleep(); skippy.sleep();
skippy.sleep();
// Challenge: Go back to lib.rs and set the `target: ` argument for each logging call to be the // Challenge: Go back to lib.rs and set the `target: ` argument for each logging call to be the
// path to the function. For example, `Frog::new` // path to the function. For example, `Frog::new`

View File

@@ -9,15 +9,15 @@ edition = "2021"
# Challenge Help 1: If you choose to take on the challenge, you'll need to add `criterion` as a # Challenge Help 1: If you choose to take on the challenge, you'll need to add `criterion` as a
# development dependency. Here is one way to do it: # development dependency. Here is one way to do it:
# [dev-dependencies] [dev-dependencies]
# criterion = { version = "0.3", features = ["html_reports"] } criterion = { version = "0.3", features = ["html_reports"] }
# Challenge Help 2: Each benchmark needs a `[[bench]]` section with a name and disabling the harness. # Challenge Help 2: Each benchmark needs a `[[bench]]` section with a name and disabling the harness.
# A name "somename" will correspond with a file "benches/somename.rs" # A name "somename" will correspond with a file "benches/somename.rs"
# [[bench]] [[bench]]
# name = "somename" name = "somename"
# harness = false harness = false
# Challenge Help 3: The Criterion documentation has a great tutorial for how to actually write your # Challenge Help 3: The Criterion documentation has a great tutorial for how to actually write your
# benchmark. Don't skip the part about `black_box()`! # benchmark. Don't skip the part about `black_box()`!

View File

@@ -0,0 +1,11 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use testing::sploosh;
pub fn sploosh_benchmark(c: &mut Criterion) {
c.bench_function("sploosh", |b| {
b.iter(|| sploosh(black_box(8), black_box(9), black_box(10)))
});
}
criterion_group!(benches, sploosh_benchmark);
criterion_main!(benches);

View File

@@ -3,6 +3,7 @@ pub fn sploosh(x: i32, y: i32, z: i32) -> i32 {
(x, _, _) if x < 0 => 99, (x, _, _) if x < 0 => 99,
(1, 2, 3) => 4, (1, 2, 3) => 4,
(5, 6, 7) => 3, (5, 6, 7) => 3,
(8, 9, 10) => 7,
(x, y, z) => x + y - z, (x, y, z) => x + y - z,
} }
} }
@@ -13,9 +14,11 @@ pub fn splish(a: i32, b: i32) -> i32 {
// 1. Use the `cfg` attribute to mark the `test` module below as a test module // 1. Use the `cfg` attribute to mark the `test` module below as a test module
#[cfg(test)]
mod test { mod test {
// 2. Bring all the library items into scope with a `use` statement // 2. Bring all the library items into scope with a `use` statement
// Hint: It's okay to use `*` here. // Hint: It's okay to use `*` here.
use super::*;
// 3. Write a test function that verifies the following condition using the `assert_eq!` or // 3. Write a test function that verifies the following condition using the `assert_eq!` or
// `assert_ne!` macros // `assert_ne!` macros
@@ -26,10 +29,24 @@ mod test {
// `cargo test` should run your tests and pass // `cargo test` should run your tests and pass
// Hint: Don't forget the `#[test]` attribute for your test function! // Hint: Don't forget the `#[test]` attribute for your test function!
#[test]
fn test_sploosh() {
assert_eq!(sploosh(1, 2, 3), 4);
assert_ne!(sploosh(5, 6, 7), 4);
assert_eq!(sploosh(-1, 2, 3), 99);
}
// 4. Write a test function that verifies the following conditions using the `assert!` macro // 4. Write a test function that verifies the following conditions using the `assert!` macro
// - splish(100, 10) is negative // - splish(100, 10) is negative
// - splish(40, 20) is positive // - splish(40, 20) is positive
// - splish(9, 3) is 0 // - splish(9, 3) is 0
#[test]
fn test_splish() {
assert!(splish(100, 10) < 0);
assert!(splish(40, 20) > 0);
assert!(splish(9, 3) == 0);
}
} }
// 5. Create a `tests/` directory and an integration test file `tests/more_tests.rs` // 5. Create a `tests/` directory and an integration test file `tests/more_tests.rs`

View File

@@ -0,0 +1,6 @@
use testing::{splish, sploosh};
#[test]
pub fn test_sploosh_splish() {
assert_eq!(sploosh(splish(-1, 0), splish(1, 1), splish(3, 2)), 4);
}

View File

@@ -22,8 +22,8 @@ fn main() {
// join handle in a variable called `handle`. Once you've done this you should be able to run // join handle in a variable called `handle`. Once you've done this you should be able to run
// the code and see the output from the child thread's expensive sum in the middle of the main // the code and see the output from the child thread's expensive sum in the middle of the main
// thread's processing of letters. // thread's processing of letters.
//
//let handle = ... let handle = thread::spawn(|| expensive_sum(my_vector));
// While the child thread is running, the main thread will also do some work // While the child thread is running, the main thread will also do some work
for letter in vec!["a", "b", "c", "d", "e", "f"] { for letter in vec!["a", "b", "c", "d", "e", "f"] {
@@ -37,17 +37,16 @@ fn main() {
// to a variable named `result` // to a variable named `result`
// - Get the i32 out of `result` and store it in a `sum` variable. // - Get the i32 out of `result` and store it in a `sum` variable.
// let result = let result = handle.join();
// let sum = let sum = result.unwrap();
// println!("The child thread's expensive sum is {}", sum); println!("The child thread's expensive sum is {}", sum);
// 3. Time for some fun with channels! // 3. Time for some fun with channels!
// - Uncomment the block comment below (Find and remove the `/*` and `*/`). // - Uncomment the block comment below (Find and remove the `/*` and `*/`).
// - Create variables `tx` and `rx` and assign them to the sending and receiving ends of an // - Create variables `tx` and `rx` and assign them to the sending and receiving ends of an
// unbounded channel. Hint: An unbounded channel can be created with `channel::unbounded()` // unbounded channel. Hint: An unbounded channel can be created with `channel::unbounded()`
/* let (tx, rx) = channel::unbounded();
// let ...
// Cloning a channel makes another variable connected to that end of the channel so that you can // Cloning a channel makes another variable connected to that end of the channel so that you can
// send it to another thread. We want another variable that can be used for sending... // send it to another thread. We want another variable that can be used for sending...
@@ -62,7 +61,7 @@ fn main() {
// Thread A // Thread A
let handle_a = thread::spawn(move || { let handle_a = thread::spawn(move || {
sleep_ms(0); sleep_ms(500);
tx2.send("Thread A: 1").unwrap(); tx2.send("Thread A: 1").unwrap();
sleep_ms(200); sleep_ms(200);
tx2.send("Thread A: 2").unwrap(); tx2.send("Thread A: 2").unwrap();
@@ -88,12 +87,40 @@ fn main() {
// 5. Oops, we forgot to join "Thread A" and "Thread B". That's bad hygiene! // 5. Oops, we forgot to join "Thread A" and "Thread B". That's bad hygiene!
// - Use the thread handles to join both threads without getting any compiler warnings. // - Use the thread handles to join both threads without getting any compiler warnings.
*/ let _ = handle_a.join();
let _ = handle_b.join();
// Challenge: Make two child threads and give them each a receiving end to a channel. From the // Challenge: Make two child threads and give them each a receiving end to a channel. From the
// main thread loop through several values and print each out and then send it to the channel. // main thread loop through several values and print each out and then send it to the channel.
// On the child threads print out the values you receive. Close the sending side in the main // On the child threads print out the values you receive. Close the sending side in the main
// thread by calling `drop(tx)` (assuming you named your sender channel variable `tx`). Join // thread by calling `drop(tx)` (assuming you named your sender channel variable `tx`). Join
// the child threads. // the child threads.
let (tx, rx) = channel::unbounded();
let rx2 = rx.clone();
let thread_a_handle = thread::spawn(move || {
for n in rx {
println!("Thread A received: {}", n);
sleep_ms(10);
}
});
let thread_b_handle = thread::spawn(move || {
for n in rx2 {
println!("Thread B received: {}", n);
sleep_ms(10);
}
});
for number in vec![1, 2, 3, 4, 5] {
println!("NUMBER: {}", number);
let _ = tx.send(number);
}
drop(tx);
let _ = thread_a_handle.join();
let _ = thread_b_handle.join();
println!("Main thread: Exiting.") println!("Main thread: Exiting.")
} }

View File

@@ -1,15 +1,39 @@
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Cake { pub enum Cake {
Chocolate, Chocolate,
MapleBacon, MapleBacon,
Spice, Spice,
} }
#[derive(Debug)]
pub struct Party { pub struct Party {
pub at_restaurant: bool, pub at_restaurant: bool,
pub num_people: u8, pub num_people: u8,
pub cake: Cake, pub cake: Cake,
} }
impl Default for Party {
fn default() -> Self {
Self {
at_restaurant: true,
num_people: 8,
cake: Cake::Chocolate,
}
}
}
impl PartialEq for Party {
fn eq(&self, other: &Self) -> bool {
self.cake == other.cake
}
}
impl From<&Party> for Cake {
fn from(party: &Party) -> Self {
party.cake
}
}
fn main() { fn main() {
// 1. The code below doesn't work because Cake doesn't implement Debug. // 1. The code below doesn't work because Cake doesn't implement Debug.
// - Derive the Debug trait for the Cake enum above so this code will work. Then, run the code. // - Derive the Debug trait for the Cake enum above so this code will work. Then, run the code.
@@ -23,11 +47,11 @@ fn main() {
// function instead of moved. // function instead of moved.
// - Hint: You may need to derive another trait in order to be able to derive the Copy trait // - Hint: You may need to derive another trait in order to be able to derive the Copy trait
// match cake { match cake {
// Cake::Chocolate => println!("The name's Chocolate. Dark...Chocolate."), Cake::Chocolate => println!("The name's Chocolate. Dark...Chocolate."),
// Cake::MapleBacon => println!("Dreams do come true!"), Cake::MapleBacon => println!("Dreams do come true!"),
// Cake::Spice => println!("Great, let's spice it up!"), Cake::Spice => println!("Great, let's spice it up!"),
// } }
// 3. Uncomment the println below. It doesn't work since the Party struct doesn't implement the // 3. Uncomment the println below. It doesn't work since the Party struct doesn't implement the
// Debug or Default traits. // Debug or Default traits.
@@ -44,18 +68,19 @@ fn main() {
// Hint: If you get stuck, there is an example at // Hint: If you get stuck, there is an example at
// https://doc.rust-lang.org/std/default/trait.Default.html#how-can-i-implement-default // https://doc.rust-lang.org/std/default/trait.Default.html#how-can-i-implement-default
// println!("The default Party is\n{:#?}", Party::default()); println!("The default Party is\n{:#?}", Party::default());
// 4. You prefer Maple Bacon cake. Use "struct update syntax" to create a Dessert with `cake` // 4. You prefer Maple Bacon cake. Use "struct update syntax" to create a Party with `cake`
// set to `Cake::MapleBacon`, but the rest of the values are default. // set to `Cake::MapleBacon`, but the rest of the values are default.
// //
// Hint: The trick to struct update syntax is specifying the value(s) you want to customize // Hint: The trick to struct update syntax is specifying the value(s) you want to customize
// first and then ending the struct with `..Default::default()` -- but no comma after that! // first and then ending the struct with `..Default::default()` -- but no comma after that!
// let party = Party { let party = Party {
// ... cake: Cake::MapleBacon,
// }; ..Default::default()
// println!("Yes! My party has my favorite {:?} cake!", party.cake); };
println!("Yes! My party has my favorite {:?} cake!", party.cake);
// 5. Parties are "equal" if they have the same cake. // 5. Parties are "equal" if they have the same cake.
// - Derive the PartialEq trait for the Cake enum so Cakes can be compared. // - Derive the PartialEq trait for the Cake enum so Cakes can be compared.
@@ -63,14 +88,15 @@ fn main() {
// then they are equal, no matter the location or number of attendees at the party. // then they are equal, no matter the location or number of attendees at the party.
// - Uncomment and run the code below. // - Uncomment and run the code below.
// let other_party = Party { let other_party = Party {
// at_restaurant: false, at_restaurant: false,
// num_people: 235, num_people: 235,
// cake: Cake::MapleBacon, cake: Cake::MapleBacon,
// }; };
// if party == other_party {
// println!("Your party is just like mine!"); if party == other_party {
// } println!("Your party is just like mine!");
}
// Challenge: You would like to be able to pass a Party struct into the smell_cake() function // Challenge: You would like to be able to pass a Party struct into the smell_cake() function
// which takes a type T which implements the Into<Cake> trait. // which takes a type T which implements the Into<Cake> trait.
@@ -78,20 +104,22 @@ fn main() {
// - Implement `From<Party> for Cake` so that the function call below works. // - Implement `From<Party> for Cake` so that the function call below works.
// //
// smell_cake(party); smell_cake(&party);
// Challenge 2: Implement `From<&Party> for Cake` so that you can smell your cake without // Challenge 2: Implement `From<&Party> for Cake` so that you can smell your cake without
// consuming it. Change the code above to pass in a &party. Then uncomment and run the code // consuming it. Change the code above to pass in a &party. Then uncomment and run the code
// below. After all, you want to smell your cake and eat it, too! // below. After all, you want to smell your cake and eat it, too!
// println!("Yum! I'm eating this cake: {:?}. Oops, I dropped it on the floor.", party.cake); println!(
// drop(cake); "Yum! I'm eating this cake: {:?}. Oops, I dropped it on the floor.",
party.cake
);
} }
pub fn admire_cake(cake: Cake) { pub fn admire_cake(cake: Cake) {
println!("What a nice {:?} cake! 🎂", cake); println!("What a nice {:?} cake! 🎂", cake);
} }
// pub fn smell_cake<T: Into<Cake>>(something: T) { pub fn smell_cake<T: Into<Cake>>(something: T) {
// println!("Hmm...something smells like a {:?} cake!", something.into()); println!("Hmm...something smells like a {:?} cake!", something.into());
// } }