Creating threads from a list of tasks - rust

I try to set up my first-ever thread machine and ran into the following issue.
We have this state:
use std::{thread, sync::mpsc};
fn main() {
let tasks = vec![String::from("task1"), String::from("task2")];
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
let handle = thread::spawn(move || {
for i in 0..(tasks.len()/2 - 1){
let send = format!("{}, {}", tasks[i], String::from("done"));
tx.send(send).unwrap();
}
});
for i in tasks.len()/2..tasks.len() - 1 {
let send = format!("{}, {}", tasks[i], String::from("done"));
tx1.send(send).unwrap();
}
handle.join().unwrap();
for string in rx {
println!("{}", string);
}
}
I try to set up parallel threads, which do something with the strings in the vector. They should work and send until the whole vector is covered. The vector has reliable 100 entries.
The corresponding error message:
error[E0382]: borrow of moved value: `tasks`
--> src/main.rs:19:14
|
6 | let tasks = vec![String::from("task1"), String::from("task2")];
| ----- move occurs because `tasks` has type `Vec<String>`, which does not implement the `Copy` trait
...
11 | let handle = thread::spawn(move || {
| ------- value moved into closure here
12 | for i in 0..(tasks.len()/2 - 1){
| ----- variable moved due to use in closure
...
19 | for i in tasks.len()/2..tasks.len() - 1 {
| ^^^^^ value borrowed here after move
error: aborting due to previous error
For more information about this error, try `rustc --explain E0382`.
It seems like I really do not understand how to set up parallel threads and how to treat channels together with threads and a list of tasks.

As the compiler indicates, the problem is that you moved the vector of tasks when spawning a thread, and then you try to borrow the vector from the main thread while it was already moved.
The problem you need to solve here is how to share the vectors' ownership between the main thread and the spawned thread. One way to do it is to use Arc.
Based on your code, I have wrote an example,
use std::{thread, sync::mpsc};
use std::sync::Arc;
fn main() {
let tasks = Arc::new(vec![String::from("task1"), String::from("task2")]);
let tasks_arc_clone = Arc::clone(&tasks);
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
let mid_idx = tasks.len() / 2 - 1;
let handle = thread::spawn(move || {
for i in 0..mid_idx {
let send = format!("{}, {}", tasks_arc_clone[i], String::from("done"));
tx.send(send).unwrap();
}
});
for i in mid_idx..tasks.len() {
let send = format!("{}, {}", tasks[i], String::from("done"));
tx1.send(send).unwrap();
}
drop(tx1);
for string in rx {
println!("{}", string);
}
handle.join().unwrap()
}
Rust playground link, in case you want to play with it.

Related

Problem regarding borrowing references in Rust

I am trying to write a program in which one thread writes to a queue and another thread
reads from the queue
But I am facing an issue regarding accessing the 'queue' in the thread that reads the queue
Below is the code which is not compiling
use ::std::collections::VecDeque;
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::thread;
use std::time::Duration;
fn main() {
//let path = std::env::args()
// .nth(1)
// .expect("Argument 1 needs to be a path");
//println!("watching {}", path);
let path = "c:\\testfolder";
if let Err(e) = watch(path) {
println!("error: {:?}", e)
}
}
fn process_queue(queue: &VecDeque<String>) -> () {}
fn watch<P: AsRef<Path>>(path: P) -> notify::Result<()> {
let (tx, rx) = std::sync::mpsc::channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
let mut queue: VecDeque<String> = VecDeque::new();
thread::spawn(|| {
// everything in here runs
process_queue(&queue)
});
watcher.watch(path.as_ref(), RecursiveMode::Recursive)?;
for res in rx {
match res {
Ok(event) => {
println!("changed: {:?}", event.paths);
let os_str: String = String::from(event.paths[0].to_str().unwrap());
//let my_str: String = os_str.unwrap().to_str().unwrap();
//let s =os_str.into_os_string();
queue.push_back(os_str);
}
Err(e) => println!("watch error: {:?}", e),
}
}
Ok(())
}
The output from the Rust compiler
error[E0373]: closure may outlive the current function, but it borrows `queue`, which is owned by the current function
--> src\main.rs:43:19
|
43 | thread::spawn(|| {
| ^^ may outlive borrowed value `queue`
...
47 | process_queue(&queue)
| ----- `queue` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src\main.rs:43:5
|
43 | / thread::spawn(|| {
44 | |
45 | | // everything in here runs
46 | |
47 | | process_queue(&queue)
48 | |
49 | | });
| |______^
help: to force the closure to take ownership of `queue` (and any other referenced variables), use the `move` keyword
|
43 | thread::spawn(move || {
| ++++
error[E0502]: cannot borrow `queue` as mutable because it is also borrowed as immutable
--> src\main.rs:63:17
|
43 | thread::spawn(|| {
| - -- immutable borrow occurs here
| _____|
| |
44 | |
45 | | // everything in here runs
46 | |
47 | | process_queue(&queue)
| | ----- first borrow occurs due to use of `queue` in closure
48 | |
49 | | });
| |______- argument requires that `queue` is borrowed for `'static`
...
63 | queue.push_back(os_str);
| ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
From the errors I understand that the compiler does not allow both mutable and immutable references at the same time.
But I don't know how to implement what I am trying to do with these restrictions.
One way to solve this is by Box-ing the VecDeque so that you can pass a cloned reference to your process_queue function.
Using a Box allows you to allocate the VecDeque on the heap so that you can give your spawned thread a reference to the Vec and also still mutate the queue in the current thread.
This would look like:
let mut queue = Box::new(VecDeque::new());
let queue_clone = queue.clone();
thread::spawn(|| {
// queue_clone is now moved into the fn closure and is
// not accessible to the code below
process_queue(queue_clone)
});
and you can update process_queue to accept the correct type:
fn process_queue(queue: Box<VecDeque<String>>) -> () { }
Note that with this implementation, process_queue only runs once when the thread is spawned, and if you want to have process_queue do something every time the queue is changed, following the advice of others to use something like Channels makes the most sense.
Thanks for all your responses
From all the responses I understand that using channels and moving the receiver loop to the other thread as suggested bu user4815162342
will be the best solution
I successfully implemented what I was trying to do using channels based on your suggestions.
The final working code is pasted below
use std::thread;
use std::time::Duration;
use notify::{RecommendedWatcher, RecursiveMode, Watcher, Config};
use std::path::Path;
use std::path::PathBuf;
//
fn main() {
//let path = std::env::args()
// .nth(1)
// .expect("Argument 1 needs to be a path");
//println!("watching {}", path);
let path="c:\\testfolder";
if let Err(e) = watch(path) {
println!("error: {:?}", e)
}
}
fn watch<P: AsRef<Path>>(path: P) -> notify::Result<()> {
let (tx, rx) = std::sync::mpsc::channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
let handle=thread::spawn(move || {
// everything in here runs
for res in rx {
match res {
Ok(event) =>{
// println!("changed: {:?}", event.paths);
let os_str:String = String::from(event.paths[0].to_str().unwrap());
println!("file name: {}", os_str);
},
Err(e) => println!("watch error: {:?}", e),
}
}
});
watcher.watch(path.as_ref(), RecursiveMode::Recursive)?;
handle.join();
Ok(())
}
In your situation, using Rust's MPSC (multi-producer single-consumer, ie essentially a queue) would probably be the best. You could also create a variable that is shared between multiple thread using Arc and Mutex structs, but that would be way overkilled and can have a performance impact (only one can access the variable at any time).
Here is an example of a multi-threaded MPSC, I will let you adapt it to your infrastructure :
use std::{sync::mpsc, thread};
fn main() {
let (sender, receiver) = mpsc::channel();
let handle_1 = thread::spawn(|| {
thread_1(sender);
});
let handle_2 = thread::spawn(|| {
thread_2(receiver);
});
handle_1.join().unwrap();
handle_2.join().unwrap();
}
// the enum must have the Send trait (automatically implemented)
enum Instruction {
Print(String),
Exit
}
fn thread_1(sender: mpsc::Sender<Instruction>) {
sender.send(Instruction::Print("I".to_owned())).unwrap();
sender.send(Instruction::Print("like".to_owned())).unwrap();
sender.send(Instruction::Print("Rust".to_owned())).unwrap();
sender.send(Instruction::Print(".".to_owned())).unwrap();
sender.send(Instruction::Exit).unwrap();
}
fn thread_2(receiver: mpsc::Receiver<Instruction>) {
'global_loop: loop {
for received in receiver.recv() {
match received {
Instruction::Print(string) => print!("{} ", string),
Instruction::Exit => {
println!("");
break 'global_loop;
}
}
}
}
}

What's the proper way to use variables defined in the main thread in a child thread in Rust?

I'm new to Rust and still reading the Rust book. Below is my program.
use clap::{App, Arg};
type GenericError = Box<dyn std::error::Error + Send + Sync + 'static>;
type GenericResult<T> = Result<T, GenericError>;
fn main() -> GenericResult<()> {
let matches = App::new("test")
.arg(Arg::new("latency")
.takes_value(true))
.get_matches();
let latency_present = matches.is_present("latency");
let latency = matches.value_of("latency").unwrap_or("1000:10,300:30");
let latency_pairs: Vec<&str> = latency.split(",").collect();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
checker.join().unwrap()?;
Ok(())
}
When I run it, it tells me this:
error[E0597]: `matches` does not live long enough
--> src\main.rs:14:19
|
14 | let latency = matches.value_of("latency").unwrap_or("1000:10,300:30");
| ^^^^^^^--------------------
| |
| borrowed value does not live long enough
| argument requires that `matches` is borrowed for `'static`
...
30 | }
| - `matches` dropped here while still borrowed
I don't quite understand the error messages here. But I guess it's because I use latency_pairs in the checker thread and latency_pairs could get dropped while checker is still executing. Is my understanding correct? How to fix the error? I tried for (i, latency_pair) in latency_pairs.clone().iter().enumerate() { in order to pass a cloned value for the thread, but it doesn't help.
latency_pairs holds references into latency which in turn references matches. Thus cloning latency_pairs just clones the references into latency and matches.
Your code would require that latency's type is &'static str but it's actually &'a str where 'a is bound to matches' lifetime.
You can call to_owned() on latency to get an owned value and split the string inside the closure or you can call to_owned() on each of the splits collected in latency_pairs and move that Vec<String> into the closure:
let latency_pairs: Vec<String> = latency.split(",").map(ToOwned::to_owned).collect();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
If you need to use latency_pairs outside of the closure, you can clone it before moving it into the closure:
let latency_pairs: Vec<String> = latency.split(",").map(ToOwned::to_owned).collect();
let latency_pairs_ = latency_pairs.clone();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs_.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
println!("{:?}", latency_pairs);

Issue with borrowing while spawning thread inside other thread

I have read the following question:
How can I run a set of functions on a recurring interval without running the same function at the same time using only the standard Rust library?
and elaborated some more complex tests.
The following code add a &str parameter for the functions and it works:
use std::{
thread,
time::{Duration, Instant},
};
fn main() {
let scheduler = thread::spawn(|| {
let wait_time = Duration::from_millis(500);
let one: &str = "Alpha";
let two: &str = "Beta";
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let thread_a = thread::spawn(move || { a(&one) });
let thread_b = thread::spawn(move || { b(&two) });
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
});
scheduler.join().expect("Scheduler panicked");
}
fn a(a: &str) {
eprintln!("{}", a);
thread::sleep(Duration::from_millis(100))
}
fn b(b: &str) {
eprintln!("{}", b);
thread::sleep(Duration::from_millis(200))
}
My understanding is that this works because Copy Trait is implemented for str.
Now consider the following example:
use std::{
thread,
time::{Duration, Instant},
};
fn main() {
let scheduler = thread::spawn(|| {
let wait_time = Duration::from_millis(500);
let one: String = String::from("Alpha");
let two: String = String::from("Beta");
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let thread_a = thread::spawn(move || { a(&one) });
let thread_b = thread::spawn(move || { b(&two) });
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
});
scheduler.join().expect("Scheduler panicked");
}
fn a(a: &str) {
eprintln!("{}", a);
thread::sleep(Duration::from_millis(100))
}
fn b(b: &str) {
eprintln!("{}", b);
thread::sleep(Duration::from_millis(200))
}
I am getting this at compile time:
error[E0382]: use of moved value: `one`
--> src\main.rs:19:42
|
10 | let one: String = String::from("Alpha");
| --- move occurs because `one` has type `String`, which does not implement the `Copy` trait
...
19 | let thread_a = thread::spawn(move || { a(&one) });
| ^^^^^^^ --- use occurs due to use in closure
| |
| value moved into closure here, in previous iteration of loop
error[E0382]: use of moved value: `two`
--> src\main.rs:20:42
|
11 | let two: String = String::from("Beta");
| --- move occurs because `two` has type `String`, which does not implement the `Copy` trait
...
20 | let thread_b = thread::spawn(move || { b(&two) });
| ^^^^^^^ --- use occurs due to use in closure
| |
| value moved into closure here, in previous iteration of loop
error: aborting due to 2 previous errors
EDIT1
It seems it can be solved with the use of .clone()
But now consider the following code:
use std::{
thread,
time::{Duration, Instant},
};
fn main() {
let one: String = String::from("Alpha");
let two: String = String::from("Beta");
let scheduler = thread::spawn(|| {
let wait_time = Duration::from_millis(500);
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let one = one.clone();
let two = two.clone();
let thread_a = thread::spawn(move || { a(&one) });
let thread_b = thread::spawn(move || { b(&two) });
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
});
scheduler.join().expect("Scheduler panicked");
}
fn a(a: &str) {
eprintln!("{}", a);
thread::sleep(Duration::from_millis(100))
}
fn b(b: &str) {
eprintln!("{}", b);
thread::sleep(Duration::from_millis(200))
}
I am now getting different error codes:
error[E0373]: closure may outlive the current function, but it borrows `two`, which is owned by the current function
--> src\main.rs:11:35
|
11 | let scheduler = thread::spawn(|| {
| ^^ may outlive borrowed value `two`
...
21 | let two = two.clone();
| --- `two` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src\main.rs:11:21
|
11 | let scheduler = thread::spawn(|| {
| _____________________^
12 | | let wait_time = Duration::from_millis(500);
13 | |
14 | | // Make this an infinite loop
... |
38 | | }
39 | | });
| |______^
help: to force the closure to take ownership of `two` (and any other referenced variables), use the `move` keyword
|
11 | let scheduler = thread::spawn(move || {
| ^^^^^^^
error[E0373]: closure may outlive the current function, but it borrows `one`, which is owned by the current function
--> src\main.rs:11:35
|
11 | let scheduler = thread::spawn(|| {
| ^^ may outlive borrowed value `one`
...
20 | let one = one.clone();
| --- `one` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src\main.rs:11:21
|
11 | let scheduler = thread::spawn(|| {
| _____________________^
12 | | let wait_time = Duration::from_millis(500);
13 | |
14 | | // Make this an infinite loop
... |
38 | | }
39 | | });
| |______^
help: to force the closure to take ownership of `one` (and any other referenced variables), use the `move` keyword
|
11 | let scheduler = thread::spawn(move || {
| ^^^^^^^
error: aborting due to 2 previous errorsù
For brevity I'll only mention one, but the same applies to two. The issue with thread::spawn(move || { a(&one) }) is that one is moved into the closure, which then results in the compile error, as one is no longer available for the next iteration.
Borrowing &one up front won't work either, because the thread borrowing one can outlive the outer thread. To get it working you can clone one (and two) before spawning the threads.
let one = one.clone();
let two = two.clone();
let thread_a = thread::spawn(move || a(&one));
let thread_b = thread::spawn(move || b(&two));
Alternatively, if you really want to borrow it, and not clone it. Then you can use, e.g. crossbeam and a scope for spawning threads. See also "How can I pass a reference to a stack variable to a thread?".
...
let one: String = String::from("Alpha");
let two: String = String::from("Beta");
let one = &one;
let two = &two;
crossbeam::scope(|scope| {
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let thread_a = scope.spawn(move |_| a(&one));
let thread_b = scope.spawn(move |_| b(&two));
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
})
.unwrap();
If you're sharing non-static immutable data among multiple threads, use Arc. That's what it's for.

Concurrent access to vector from multiple threads using a mutex lock

I'm using an example provided by the Tokio library and attempting to have a vector of all the currently active TCP connections. Ultimately, I would like to be able to broadcast a message to each of the active connections, by looping through them and writing a message to the socket.
To start with, I am trying to print out the current number of connections in one thread whilst accepting connections in another.
To do this, I'm trying to use a shared vector. I've not yet implemented the removal of connections from the vector as and when they disconnect.
// A tiny async echo server with tokio-core
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;
use futures::{Future, Stream};
use tokio_io::{io, AsyncRead};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;
use std::thread;
use std::sync::{Arc, Mutex};
use std::io::stdout;
use std::io::Write;
fn main() {
// Create the event loop that will drive this server
let mut core = Core::new().unwrap();
let handle = core.handle();
// Bind the server's socket
let addr = "127.0.0.1:12345".parse().unwrap();
let tcp = TcpListener::bind(&addr, &handle).unwrap();
let mut connections = Arc::new((Mutex::new(Vec::new())));
thread::spawn(move || {
//Every 10 seconds print out the current number of connections
let mut i;
loop {
i = connections.lock().unwrap().len();
println!("There are {} connections", i);
stdout().flush();
thread::sleep_ms(10000);
}
});
// Iterate incoming connections
let server = tcp.incoming().for_each(|(tcp, _)| {
connections.lock().unwrap().push(tcp);
// Split up the read and write halves
let (reader, writer) = tcp.split();
// Future of the copy
let bytes_copied = io::copy(reader, writer);
// ... after which we'll print what happened
let handle_conn = bytes_copied.map(|(n, _, _)| {
println!("wrote {} bytes", n)
}).map_err(|err| {
println!("IO error {:?}", err)
});
// Spawn the future as a concurrent task
handle.spawn(handle_conn);
Ok(())
});
// Spin up the server on the event loop
core.run(server).unwrap();
}
At the moment this is failing to build with the following errors:
error[E0382]: capture of moved value: `connections`
--> src/main.rs:36:42
|
26 | thread::spawn(move || {
| ------- value moved (into closure) here
...
36 | let server = tcp.incoming().for_each(|(tcp, _)| {
| ^^^^^^^^^^ value captured here after move
|
= note: move occurs because `connections` has type `std::sync::Arc<std::sync::Mutex<std::vec::Vec<tokio_core::net::TcpStream>>>`, which does not implement the `Copy` trait
error[E0382]: use of moved value: `tcp`
--> src/main.rs:40:32
|
38 | connections.lock().unwrap().push(tcp);
| --- value moved here
39 | // Split up the read and write halves
40 | let (reader, writer) = tcp.split();
| ^^^ value used here after move
|
= note: move occurs because `tcp` has type `tokio_core::net::TcpStream`, which does not implement the `Copy` trait
Is it possible to achieve this without writing any unsafe code?
You get the first error because of the move closure:
let mut connections = Arc::new((Mutex::new(Vec::new())));
thread::spawn(move || {
let mut i = connections.lock().unwrap().len();
....
}
This actually moves the whole Arc, while you only want to move "a part" of it (that is, move it in such a way that the reference count is incremented, and that both threads can use it).
To do this, we can use Arc::clone:
let mut connections = Arc::new((Mutex::new(Vec::new())));
let conn = connections.clone();
thread::spawn(move || {
let mut i = conn.lock().unwrap().len();
....
}
This way, the cloned Arc, conn, is moved into the closure, and the original Arc, connections, is not, and hence still usable.
I'm not sure exactly what you are doing with your second error, but for the sake of simply counting the connections you do not need to push the entire thing.

Copying in Vectors to a Thread

Considering the following code, I wish to have access to both client and requests within my thread, currently I do not:
for _x in 0..100 {
let handle = thread::spawn(move || {
let start = time::precise_time_s();
let res = client.get("http://jacob.uk.com")
.header(Connection::close())
.send().unwrap();
let end = time::precise_time_s();
requests.push(Request::new(end-start));
});
handle.join().unwrap()
}
I get the following compiler error:
Compiling Herd v0.1.0 (file:///Users/jacobclark/Desktop/LearningRust/Herd)
src/main.rs:41:23: 41:29 error: capture of moved value: `client`
src/main.rs:41 let res = client.get("http://jacob.uk.com")
^~~~~~
src/main.rs:38:41: 48:10 note: `client` moved into closure environment here because it has type `[closure(())]`, which is non-copyable
src/main.rs:38 let handle = thread::spawn(move || {
src/main.rs:39 let start = time::precise_time_s();
src/main.rs:40
src/main.rs:41 let res = client.get("http://jacob.uk.com")
src/main.rs:42 .header(Connection::close())
src/main.rs:43 .send().unwrap();
...
src/main.rs:38:41: 48:10 help: perhaps you meant to use `clone()`?
src/main.rs:47:13: 47:21 error: capture of moved value: `requests`
src/main.rs:47 requests.push(Request::new(end-start));
^~~~~~~~
src/main.rs:38:41: 48:10 note: `requests` moved into closure environment here because it has type `[closure(())]`, which is non-copyable
src/main.rs:38 let handle = thread::spawn(move || {
src/main.rs:39 let start = time::precise_time_s();
src/main.rs:40
src/main.rs:41 let res = client.get("http://jacob.uk.com")
src/main.rs:42 .header(Connection::close())
src/main.rs:43 .send().unwrap();
...
src/main.rs:38:41: 48:10 help: perhaps you meant to use `clone()`?
src/main.rs:53:24: 53:32 error: use of moved value: `requests`
src/main.rs:53 Request::mean_time(requests);
^~~~~~~~
src/main.rs:38:41: 48:10 note: `requests` moved into closure environment here because it has type `[closure(())]`, which is non-copyable
src/main.rs:38 let handle = thread::spawn(move || {
src/main.rs:39 let start = time::precise_time_s();
src/main.rs:40
src/main.rs:41 let res = client.get("http://jacob.uk.com")
src/main.rs:42 .header(Connection::close())
src/main.rs:43 .send().unwrap();
...
src/main.rs:38:41: 48:10 help: perhaps you meant to use `clone()`?
error: aborting due to 3 previous errors
Could not compile `Herd`
.
Firstly, minimal examples are really useful:
use std::thread;
fn main() {
let mut items = Vec::new();
for _ in 0..100 {
thread::spawn(move || {
items.push(());
});
}
}
So what's the problem here? Well, you're moving items into a closure 100 times - but you can only move it once!
To share data across multiple threads, you need
To remove data races - put it in a Mutex (or don't mutate it).
To keep it alive - put it in an Arc (Atomically Reference-Counted pointer).
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let items = Arc::new(Mutex::new(Vec::new()));
for _ in 0..10 {
let thread_items = items.clone();
let handle = thread::spawn(move || {
thread_items.lock().unwrap().push(());
});
handle.join().unwrap();
}
println!("{:?}", items);
}

Resources