rustlings
rustlings copied to clipboard
Threads1.rs hint references "sample answers"; where are they?
The Threads1.rs hint references "sample answers". I looked through the project and have not found them. Are sample answers something that existed in a previous version and no longer exist?
i think they meant from the book. i solved it when i read the chapter on Threads in Rust by example. i'll give you links, i know you can figure it out. :)
https://stevedonovan.github.io/rust-gentle-intro/7-shared-and-networking.html#shared-state https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct
As you might suspect, Mutex<T> is a smart pointer. More accurately, the call to lock returns a smart pointer called MutexGuard, wrapped in a LockResult that we handled with the call to unwrap. The MutexGuard smart pointer implements Deref to point at our inner data; the smart pointer also has a Drop implementation that releases the lock automatically when a MutexGuard goes out of scope, which happens at the end of the inner scope in Listing 16-12. As a result, we don’t risk forgetting to release the lock and blocking the mutex from being used by other threads because the lock release happens automatically.
I also thought it meant full exercise solutions, like #293. Maybe change it to say "sample answers from the book"?
threads1.rs is the first exercise where I got frustrated.
SPOILERS
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
struct JobStatus {
jobs_completed: u32,
}
fn main() {
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
thread::spawn(move || {
for _ in 0..10 {
thread::sleep(Duration::from_millis(250));
status.clone().lock().unwrap().jobs_completed += 1;
}
});
/*
// I haven't been able to get this part to work
while status.lock().unwrap().jobs_completed < 10 {
// so I've resorted to this hack in frustration
*/
let mut counter = 0;
loop {
if counter >= 6 {break;}; counter += 1;
println!("waiting... {} ", counter);
thread::sleep(Duration::from_millis(500));
}
}
By the time I resorted to the hint
I had already wrapped the data in a Mutex so the first half of the hint did not progress me at all, (though it was reassuring to know that I was on the right track... maybe?)
The hint then mentioned "We can use Arc<T> to fix this." which duckduckgo tells me does not appear anywhere in the second edition of The Rust book. (I think I tracked down to what the hint was referring: First edition: concurrency
(which also didn't help solve the while status.jobs_completed < 10
part of the exercise.)
I think that the hint needs to be updated to match the current documentation.
This one was so tough. arc1.rs being couple of days away in my voyage through rustlings meant I didn't remember the exercise fully anymore, and needed to take a peek at it.
Yet even then I just couldn't do it while keeping the 'while' loop. It seems to make the locking harder to manage than with other loop structures in that part of the exercise. Changing that or hinting at the possibility of change there could reduce the problem space a bit. (I would still love to see a solution with the "while", to see what options I missed.)
Additionally indeed the "6" could be done away with by preparing a println!("At {} jobs we are now finished!", status.jobs_completed)
for a more explicit completion criteria.
@Lynoure Here's my solution to the arc1.rs exercise. (If you search github for rustlings solutions
or rustlings answers
it seems that there are a few other examples. You could clone the rustlings repo and add your solutions. I'm intrigued to see a solution without the while
loop.)
Can someone confirm that this is a good idiomatic solution for the threads1.rs exercise?
@alexxroche Sorry I was unclear! I meant looking at my own solution for arc1 helped already yesterday, but I would have had easier time altogether if those exercises had been right after each other, as they are so closely related.
That solution looks beautiful indeed. As I had not yet a clear enough understanding of when things go out of scope in Rust, it would have never occurred to me. I kept trying to unlock in some way before the wait. So I could not with good conscience claim any of my solutions as 'idiomatic'!
Spoilers
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
struct JobStatus {
jobs_completed: u32,
}
fn main() {
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
let status_shared = Arc::clone(&status);
thread::spawn(move || {
for _ in 0..10 { // NOTE: It going up to 11? That would be ridiculous!
thread::sleep(Duration::from_millis(250));
let mut s = status_shared.lock().unwrap();
s.jobs_completed += 1;
}
});
loop {
thread::sleep(Duration::from_millis(500));
let s = status.lock().unwrap();
if s.jobs_completed < 10 {
println!("Waiting... {} so far", s.jobs_completed);
} else {
println!{"At {} we are done!", s.jobs_completed}
break;
}
}
}
I don't know if I understood this exercise completely.
Particularly this line:
Because of the difference between the spawned threads' sleep time, and the waiting threads sleep time, when you see 6 lines of "waiting..." and the program ends without timing out when running.
This confused me a bit, and could probably be worded a bit better.
I was able to solve the solution without changing the original code much, after reading both the book and the Rust by example.
Spoiler
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
struct JobStatus {
jobs_completed: u32,
}
fn main() {
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
let status_shared = status.clone();
thread::spawn(move || {
for _ in 0..10 {
let mut status = status_shared.lock().unwrap();
thread::sleep(Duration::from_millis(250));
status.jobs_completed += 1;
}
});
while status.lock().unwrap().jobs_completed < 10 {
println!("waiting... ");
thread::sleep(Duration::from_millis(500));
}
}
I don't know if this is the solution that was intended, but it compiled for me.
@fnky your solution is not complete. The phrase you are quoting means that you should see "waiting..." 6 times and then exit nicely to have solved the exercise. If you move your line let mut status = status_shared.lock().unwrap()
to after the sleep, you will get the behaviour that is described in the text.
This was the first truly frustrating exercise for me. I kept trying different things but meeting with different errors that I couldn't quite wrap my head around.
Looking at this reply helped me get past my issue with minimal code changes. I see some spoiler solutions here that remove the while
loop but it is not necessary.
My solution and explanation (Spoiler)
Because status
is now an Arc<Mutex<JobStatus>>
, we must use lock()
and unwrap()
in the while
loop condition to be able to reference the jobs_completed
property.
We must clone status
because whatever variable we reference for the status in the thread context will be moved into the thread's context and thus we will no longer be able to use it outside of the thread::spawn
context. Take a look at what the move
keyword does in the book appendix to see why we need to have a separate variable for holding the shared state.
use std::sync::{ Arc, Mutex }; // first change
use std::thread;
use std::time::Duration;
struct JobStatus {
jobs_completed: u32,
}
fn main() {
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); // second change
let status_shared = status.clone();
thread::spawn(move || {
for _ in 0..10 {
thread::sleep(Duration::from_millis(250));
status_shared.lock().unwrap().jobs_completed += 1; // third change
}
});
while status.lock().unwrap().jobs_completed < 10 { // fourth change
println!("waiting... ");
thread::sleep(Duration::from_millis(500));
}
}