Compare commits

...

10 commits

Author SHA1 Message Date
mo8it 09c94bef2d clippy3 solution 2024-07-01 12:09:52 +02:00
mo8it a0e810b471 clippy2 solution 2024-07-01 11:55:18 +02:00
mo8it 78728d5238 clippy1 solution 2024-07-01 11:54:35 +02:00
mo8it cc2c0958c9 macros4 solution 2024-07-01 11:54:05 +02:00
mo8it 4cb15a4cda macros3 solution 2024-07-01 11:37:48 +02:00
mo8it 9845e046de macros2 solution 2024-07-01 11:31:37 +02:00
mo8it cf90364fd7 macros1 solution 2024-07-01 11:28:38 +02:00
mo8it a13e3cd07f threads3 solution 2024-07-01 11:23:40 +02:00
mo8it dfa2b44f71 threads2 solution 2024-07-01 11:11:11 +02:00
mo8it b000164eed threads1 solution 2024-07-01 10:59:33 +02:00
21 changed files with 326 additions and 81 deletions

View file

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

View file

@ -1,35 +1,34 @@
// 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_completed
// work. But this time, the spawned threads need to be in charge of updating a
// shared value: `JobStatus.jobs_done`
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use std::{sync::Arc, thread, time::Duration};
struct JobStatus {
jobs_completed: u32,
jobs_done: u32,
}
fn main() {
// TODO: `Arc` isn't enough if you want a **mutable** shared state
let status = Arc::new(JobStatus { jobs_completed: 0 });
// TODO: `Arc` isn't enough if you want a **mutable** shared state.
let status = Arc::new(JobStatus { jobs_done: 0 });
let mut handles = vec![];
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));
// TODO: You must take an action before you update a shared value
status_shared.jobs_completed += 1;
// TODO: You must take an action before you update a shared value.
status_shared.jobs_done += 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_completed`
println!("Jobs completed: {}", ???);
// TODO: Print the value of `JobStatus.jobs_done`.
println!("Jobs done: {}", todo!());
}

View file

@ -1,7 +1,4 @@
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use std::{sync::mpsc, thread, time::Duration};
struct Queue {
length: u32,
@ -11,7 +8,7 @@ struct Queue {
impl Queue {
fn new() -> Self {
Queue {
Self {
length: 10,
first_half: vec![1, 2, 3, 4, 5],
second_half: vec![6, 7, 8, 9, 10],
@ -19,20 +16,22 @@ impl Queue {
}
}
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) -> () {
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?
thread::spawn(move || {
for val in q.first_half {
println!("sending {:?}", val);
println!("Sending {val:?}");
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
thread::sleep(Duration::from_millis(250));
}
});
thread::spawn(move || {
for val in q.second_half {
println!("sending {:?}", val);
println!("Sending {val:?}");
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
thread::sleep(Duration::from_millis(250));
}
});
}
@ -55,11 +54,11 @@ mod tests {
let mut total_received: u32 = 0;
for received in rx {
println!("Got: {}", received);
println!("Got: {received}");
total_received += 1;
}
println!("total numbers received: {}", total_received);
println!("Number of received values: {total_received}");
assert_eq!(total_received, queue_length);
}
}

View file

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

View file

@ -2,6 +2,7 @@ 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 @@
// Make me compile, without taking the macro out of the module!
// TODO: Fix the compiler error without taking the macro definition out of this
// module.
mod macros {
macro_rules! my_macro {
() => {

View file

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

View file

@ -1,19 +1,17 @@
// 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;
use std::f32::consts::PI;
fn main() {
let pi = 3.14f32;
let radius = 5.00f32;
// Use the more accurate `PI` constant.
let pi = PI;
let radius: f32 = 5.0;
let area = pi * f32::powi(radius, 2);
let area = pi * radius.powi(2);
println!(
"The area of a circle with radius {:.2} is {:.5}!",
radius, area
)
println!("The area of a circle with radius {radius:.2} is {area:.5}");
}

View file

@ -1,8 +1,10 @@
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,25 +1,27 @@
// Here's a couple more easy Clippy fixes, so you can see its utility.
// Here are some more easy Clippy fixes so you can see its utility 📎
// TODO: Fix all the Clippy lints.
#[rustfmt::skip]
#[allow(unused_variables, unused_assignments)]
fn main() {
let my_option: Option<()> = None;
if my_option.is_none() {
my_option.unwrap();
println!("{:?}", 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 b: {}", value_a, value_b);
println!("value a: {value_a}; value b: {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 completed.
finish before the spawned threads are done.
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_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:
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:
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_completed: 0 }));
let status = Arc::new(Mutex::new(JobStatus { jobs_done: 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,10 +1076,11 @@ 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.
See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info."""
Related section in The Book:
https://doc.rust-lang.org/book/ch16-02-message-passing.html"""
# MACROS
@ -1088,9 +1089,8 @@ 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. If you're stuck, take a look at what's inside
`my_macro`."""
When you call a macro, you need to add something special compared to a regular
function call."""
[[exercises]]
name = "macros2"
@ -1109,10 +1109,7 @@ 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.
The same trick also works on "extern crate" statements for crates that have
exported macros, if you've seen any of those around."""
special to the module to lift the macro out into its parent."""
[[exercises]]
name = "macros4"
@ -1137,7 +1134,7 @@ dir = "22_clippy"
test = false
strict_clippy = true
hint = """
Rust stores the highest precision version of any long or infinite precision
Rust stores the highest precision version of some long or infinite precision
mathematical constants in the Rust standard library:
https://doc.rust-lang.org/stable/std/f32/consts/index.html
@ -1145,7 +1142,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 compile output and use the
See the suggestions of the Clippy warning in the compile output and use the
appropriate replacement constant from `std::f32::consts`..."""
[[exercises]]
@ -1154,7 +1151,8 @@ dir = "22_clippy"
test = false
strict_clippy = true
hint = """
`for` loops over `Option` values are more clearly expressed as an `if let`"""
`for` loops over `Option` values are more clearly expressed as an `if-let`
statement."""
[[exercises]]
name = "clippy3"

View file

@ -1 +1,37 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
// 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");
}
}

View file

@ -1 +1,41 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
// 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);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}

View file

@ -1 +1,66 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
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);
}
}

View file

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

View file

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

View file

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

View file

@ -1 +1,15 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
// 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);
}

View file

@ -1 +1,17 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
// 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}");
}

View file

@ -1 +1,10 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
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}");
}

View file

@ -1 +1,31 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
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);
}