Compare commits

..

No commits in common. "09c94bef2dbaf44daf81d8f618289c9425d1f90f" and "663a03a17b2d2001f4f3f35a59cd2e2aa5f2bb24" have entirely different histories.

21 changed files with 81 additions and 326 deletions

View file

@ -3,35 +3,31 @@
// wait until all the spawned threads have finished and should collect their
// return values into a vector.
use std::{
thread,
time::{Duration, Instant},
};
use std::thread;
use std::time::{Duration, Instant};
fn main() {
let mut handles = Vec::new();
let mut handles = vec![];
for i in 0..10 {
let handle = thread::spawn(move || {
handles.push(thread::spawn(move || {
let start = Instant::now();
thread::sleep(Duration::from_millis(250));
println!("Thread {i} done");
println!("thread {} is complete", i);
start.elapsed().as_millis()
});
handles.push(handle);
}));
}
let mut results = Vec::new();
let mut results: Vec<u128> = vec![];
for handle in handles {
// TODO: Collect the results of all threads into the `results` vector.
// Use the `JoinHandle` struct which is returned by `thread::spawn`.
// TODO: a struct is returned from thread::spawn, can you use it?
}
if results.len() != 10 {
panic!("Oh no! Some thread isn't done yet!");
panic!("Oh no! All the spawned threads did not finish!");
}
println!();
for (i, result) in results.into_iter().enumerate() {
println!("Thread {i} took {result}ms");
println!("thread {} took {}ms", i, result);
}
}

View file

@ -1,34 +1,35 @@
// Building on the last exercise, we want all of the threads to complete their
// work. But this time, the spawned threads need to be in charge of updating a
// shared value: `JobStatus.jobs_done`
// work but this time the spawned threads need to be in charge of updating a
// shared value: JobStatus.jobs_completed
use std::{sync::Arc, thread, time::Duration};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
struct JobStatus {
jobs_done: u32,
jobs_completed: u32,
}
fn main() {
// TODO: `Arc` isn't enough if you want a **mutable** shared state.
let status = Arc::new(JobStatus { jobs_done: 0 });
// TODO: `Arc` isn't enough if you want a **mutable** shared state
let status = Arc::new(JobStatus { jobs_completed: 0 });
let mut handles = Vec::new();
let mut handles = vec![];
for _ in 0..10 {
let status_shared = Arc::clone(&status);
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(250));
// TODO: You must take an action before you update a shared value.
status_shared.jobs_done += 1;
// TODO: You must take an action before you update a shared value
status_shared.jobs_completed += 1;
});
handles.push(handle);
}
// Waiting for all jobs to complete.
// Waiting for all jobs to complete
for handle in handles {
handle.join().unwrap();
}
// TODO: Print the value of `JobStatus.jobs_done`.
println!("Jobs done: {}", todo!());
// TODO: Print the value of `JobStatus.jobs_completed`
println!("Jobs completed: {}", ???);
}

View file

@ -1,4 +1,7 @@
use std::{sync::mpsc, thread, time::Duration};
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
struct Queue {
length: u32,
@ -8,7 +11,7 @@ struct Queue {
impl Queue {
fn new() -> Self {
Self {
Queue {
length: 10,
first_half: vec![1, 2, 3, 4, 5],
second_half: vec![6, 7, 8, 9, 10],
@ -16,22 +19,20 @@ impl Queue {
}
}
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) {
// TODO: We want to send `tx` to both threads. But currently, it is moved
// into the frist thread. How could you solve this problem?
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) -> () {
thread::spawn(move || {
for val in q.first_half {
println!("Sending {val:?}");
println!("sending {:?}", val);
tx.send(val).unwrap();
thread::sleep(Duration::from_millis(250));
thread::sleep(Duration::from_secs(1));
}
});
thread::spawn(move || {
for val in q.second_half {
println!("Sending {val:?}");
println!("sending {:?}", val);
tx.send(val).unwrap();
thread::sleep(Duration::from_millis(250));
thread::sleep(Duration::from_secs(1));
}
});
}
@ -54,11 +55,11 @@ mod tests {
let mut total_received: u32 = 0;
for received in rx {
println!("Got: {received}");
println!("Got: {}", received);
total_received += 1;
}
println!("Number of received values: {total_received}");
println!("total numbers received: {}", total_received);
assert_eq!(total_received, queue_length);
}
}

View file

@ -5,6 +5,5 @@ macro_rules! my_macro {
}
fn main() {
// TODO: Fix the macro call.
my_macro();
}

View file

@ -2,7 +2,6 @@ fn main() {
my_macro!();
}
// TODO: Fix the compiler error by moving the whole definition of this macro.
macro_rules! my_macro {
() => {
println!("Check out my macro!");

View file

@ -1,5 +1,5 @@
// TODO: Fix the compiler error without taking the macro definition out of this
// module.
// Make me compile, without taking the macro out of the module!
mod macros {
macro_rules! my_macro {
() => {

View file

@ -1,4 +1,3 @@
// TODO: Fix the compiler error by adding one or two characters.
#[rustfmt::skip]
macro_rules! my_macro {
() => {

View file

@ -1,17 +1,19 @@
// The Clippy tool is a collection of lints to analyze your code so you can
// catch common mistakes and improve your Rust code.
//
// For these exercises, the code will fail to compile when there are Clippy
// For these exercises the code will fail to compile when there are Clippy
// warnings. Check Clippy's suggestions from the output to solve the exercise.
use std::f32::consts::PI;
use std::f32;
fn main() {
// Use the more accurate `PI` constant.
let pi = PI;
let radius: f32 = 5.0;
let pi = 3.14f32;
let radius = 5.00f32;
let area = pi * radius.powi(2);
let area = pi * f32::powi(radius, 2);
println!("The area of a circle with radius {radius:.2} is {area:.5}");
println!(
"The area of a circle with radius {:.2} is {:.5}!",
radius, area
)
}

View file

@ -1,10 +1,8 @@
fn main() {
let mut res = 42;
let option = Some(12);
// TODO: Fix the Clippy lint.
for x in option {
res += x;
}
println!("{res}");
println!("{}", res);
}

View file

@ -1,27 +1,25 @@
// Here are some more easy Clippy fixes so you can see its utility 📎
// TODO: Fix all the Clippy lints.
// Here's a couple more easy Clippy fixes, so you can see its utility.
#[rustfmt::skip]
#[allow(unused_variables, unused_assignments)]
fn main() {
let my_option: Option<()> = None;
if my_option.is_none() {
println!("{:?}", my_option.unwrap());
my_option.unwrap();
}
let my_arr = &[
-1, -2, -3
-4, -5, -6
];
println!("My array! Here it is: {my_arr:?}");
println!("My array! Here it is: {:?}", my_arr);
let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
println!("This Vec is empty, see? {my_empty_vec:?}");
println!("This Vec is empty, see? {:?}", my_empty_vec);
let mut value_a = 45;
let mut value_b = 66;
// Let's swap these two!
value_a = value_b;
value_b = value_a;
println!("value a: {value_a}; value b: {value_b}");
println!("value a: {}; value b: {}", value_a, value_b);
}

View file

@ -1037,7 +1037,7 @@ hint = """
https://doc.rust-lang.org/std/thread/fn.spawn.html
A challenge with multi-threaded applications is that the main thread can
finish before the spawned threads are done.
finish before the spawned threads are completed.
https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles
Use the `JoinHandle`s to wait for each thread to finish and collect their
@ -1051,19 +1051,19 @@ dir = "20_threads"
test = false
hint = """
`Arc` is an Atomic Reference Counted pointer that allows safe, shared access
to **immutable** data. But we want to *change* the number of `jobs_done` so
we'll need to also use another type that will only allow one thread to mutate
the data at a time. Take a look at this section of the book:
to **immutable** data. But we want to *change* the number of `jobs_completed`
so we'll need to also use another type that will only allow one thread to
mutate the data at a time. Take a look at this section of the book:
https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct
Keep reading if you'd like more hints :)
Do you now have an `Arc<Mutex<JobStatus>>` at the beginning of `main`? Like:
```
let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 }));
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
```
Similar to the code in the following example in The Book:
Similar to the code in the following example in the book:
https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads"""
[[exercises]]
@ -1076,11 +1076,10 @@ An alternate way to handle concurrency between threads is to use an `mpsc`
With both a sending end and a receiving end, it's possible to send values in
one thread and receive them in another.
Multiple producers are possible by using `clone()` to create a duplicate of the
Multiple producers are possible by using clone() to create a duplicate of the
original sending end.
Related section in The Book:
https://doc.rust-lang.org/book/ch16-02-message-passing.html"""
See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info."""
# MACROS
@ -1089,8 +1088,9 @@ name = "macros1"
dir = "21_macros"
test = false
hint = """
When you call a macro, you need to add something special compared to a regular
function call."""
When you call a macro, you need to add something special compared to a
regular function call. If you're stuck, take a look at what's inside
`my_macro`."""
[[exercises]]
name = "macros2"
@ -1109,7 +1109,10 @@ dir = "21_macros"
test = false
hint = """
In order to use a macro outside of its module, you need to do something
special to the module to lift the macro out into its parent."""
special to the module to lift the macro out into its parent.
The same trick also works on "extern crate" statements for crates that have
exported macros, if you've seen any of those around."""
[[exercises]]
name = "macros4"
@ -1134,7 +1137,7 @@ dir = "22_clippy"
test = false
strict_clippy = true
hint = """
Rust stores the highest precision version of some long or infinite precision
Rust stores the highest precision version of any long or infinite precision
mathematical constants in the Rust standard library:
https://doc.rust-lang.org/stable/std/f32/consts/index.html
@ -1142,7 +1145,7 @@ We may be tempted to use our own approximations for certain mathematical
constants, but clippy recognizes those imprecise mathematical constants as a
source of potential error.
See the suggestions of the Clippy warning in the compile output and use the
See the suggestions of the clippy warning in compile output and use the
appropriate replacement constant from `std::f32::consts`..."""
[[exercises]]
@ -1151,8 +1154,7 @@ dir = "22_clippy"
test = false
strict_clippy = true
hint = """
`for` loops over `Option` values are more clearly expressed as an `if-let`
statement."""
`for` loops over `Option` values are more clearly expressed as an `if let`"""
[[exercises]]
name = "clippy3"

View file

@ -1,37 +1 @@
// This program spawns multiple threads that each run for at least 250ms, and
// each thread returns how much time they took to complete. The program should
// wait until all the spawned threads have finished and should collect their
// return values into a vector.
use std::{
thread,
time::{Duration, Instant},
};
fn main() {
let mut handles = Vec::new();
for i in 0..10 {
let handle = thread::spawn(move || {
let start = Instant::now();
thread::sleep(Duration::from_millis(250));
println!("Thread {i} done");
start.elapsed().as_millis()
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
// Collect the results of all threads into the `results` vector.
results.push(handle.join().unwrap());
}
if results.len() != 10 {
panic!("Oh no! Some thread isn't done yet!");
}
println!();
for (i, result) in results.into_iter().enumerate() {
println!("Thread {i} took {result}ms");
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,41 +1 @@
// Building on the last exercise, we want all of the threads to complete their
// work. But this time, the spawned threads need to be in charge of updating a
// shared value: `JobStatus.jobs_done`
use std::{
sync::{Arc, Mutex},
thread,
time::Duration,
};
struct JobStatus {
jobs_done: u32,
}
fn main() {
// `Arc` isn't enough if you want a **mutable** shared state.
// We need to wrap the value with a `Mutex`.
let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 }));
// ^^^^^^^^^^^ ^
let mut handles = Vec::new();
for _ in 0..10 {
let status_shared = Arc::clone(&status);
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(250));
// Lock before you update a shared value.
status_shared.lock().unwrap().jobs_done += 1;
// ^^^^^^^^^^^^^^^^
});
handles.push(handle);
}
// Waiting for all jobs to complete.
for handle in handles {
handle.join().unwrap();
}
println!("Jobs done: {}", status.lock().unwrap().jobs_done);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,66 +1 @@
use std::{sync::mpsc, thread, time::Duration};
struct Queue {
length: u32,
first_half: Vec<u32>,
second_half: Vec<u32>,
}
impl Queue {
fn new() -> Self {
Self {
length: 10,
first_half: vec![1, 2, 3, 4, 5],
second_half: vec![6, 7, 8, 9, 10],
}
}
}
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) {
// Clone the sender `tx` first.
let tx_clone = tx.clone();
thread::spawn(move || {
for val in q.first_half {
println!("Sending {val:?}");
// Then use the clone in the first thread. This means that
// `tx_clone` is moved to the first thread and `tx` to the second.
tx_clone.send(val).unwrap();
thread::sleep(Duration::from_millis(250));
}
});
thread::spawn(move || {
for val in q.second_half {
println!("Sending {val:?}");
tx.send(val).unwrap();
thread::sleep(Duration::from_millis(250));
}
});
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn threads3() {
let (tx, rx) = mpsc::channel();
let queue = Queue::new();
let queue_length = queue.length;
send_tx(queue, tx);
let mut total_received: u32 = 0;
for received in rx {
println!("Got: {received}");
total_received += 1;
}
println!("Number of received values: {total_received}");
assert_eq!(total_received, queue_length);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,10 +1 @@
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
fn main() {
my_macro!();
// ^
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,10 +1 @@
// Moved the macro definition to be before its call.
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
fn main() {
my_macro!();
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,13 +1 @@
// Added the attribute `macro_use` attribute.
#[macro_use]
mod macros {
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
}
fn main() {
my_macro!();
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,15 +1 @@
// Added semicolons to separate the macro arms.
#[rustfmt::skip]
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
($val:expr) => {
println!("Look at this other macro: {}", $val);
};
}
fn main() {
my_macro!();
my_macro!(7777);
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,17 +1 @@
// The Clippy tool is a collection of lints to analyze your code so you can
// catch common mistakes and improve your Rust code.
//
// For these exercises, the code will fail to compile when there are Clippy
// warnings. Check Clippy's suggestions from the output to solve the exercise.
use std::f32::consts::PI;
fn main() {
// Use the more accurate `PI` constant.
let pi = PI;
let radius: f32 = 5.0;
let area = pi * radius.powi(2);
println!("The area of a circle with radius {radius:.2} is {area:.5}");
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,10 +1 @@
fn main() {
let mut res = 42;
let option = Some(12);
// Use `if-let` instead of iteration.
if let Some(x) = option {
res += x;
}
println!("{res}");
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,31 +1 @@
use std::mem;
#[rustfmt::skip]
#[allow(unused_variables, unused_assignments)]
fn main() {
let my_option: Option<()> = None;
// `unwrap` of an `Option` after checking if it is `None` will panic.
// Use `if-let` instead.
if let Some(value) = my_option {
println!("{value:?}");
}
// A comma was missing.
let my_arr = &[
-1, -2, -3,
-4, -5, -6,
];
println!("My array! Here it is: {:?}", my_arr);
let mut my_empty_vec = vec![1, 2, 3, 4, 5];
// `resize` mutates a vector instead of returning a new one.
// `resize(0, …)` clears a vector, so it is better to use `clear`.
my_empty_vec.clear();
println!("This Vec is empty, see? {my_empty_vec:?}");
let mut value_a = 45;
let mut value_b = 66;
// Use `mem::swap` to correctly swap two values.
mem::swap(&mut value_a, &mut value_b);
println!("value a: {}; value b: {}", value_a, value_b);
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰