blippy
blippy

Reputation: 1540

Why doesn't this compile - use of undeclared type name `thread::scoped`

I'm trying to get my head around Rust. I've got an alpha version of 1.

Here's the problem I'm trying to program: I have a vector of floats. I want to set up some threads asynchronously. Each thread should wait for the number of seconds specified by each element of the vector, and return the value of the element, plus 10. The results need to be in input order.

It's an artificial example, to be sure, but I wanted to see if I could implement something simple before moving onto more complex code. Here is my code so far:

use std::thread;
use std::old_io::timer;
use std::time::duration::Duration;

fn main() {
    let mut vin = vec![1.4f64, 1.2f64, 1.5f64];
    let mut guards: Vec<thread::scoped> = Vec::with_capacity(3);
    let mut answers: Vec<f64> = Vec::with_capacity(3);

    for i in 0..3 {
        guards[i] = thread::scoped( move || {
            let ms = (1000.0f64 * vin[i]) as i64;
            let d = Duration::milliseconds(ms);
            timer::sleep(d);
            println!("Waited {}", vin[i]);
            answers[i] = 10.0f64 + (vin[i] as f64);
        })};

    for i in 0..3 {guards[i].join(); };

    for i in 0..3 {println!("{}", vin[i]); }

}

So the input vector is [1.4, 1.2, 1.5], and I'm expecting the output vector to be [11.4, 11.2, 11.5].

There appear to be a number of problems with my code, but the first one is that I get a compilation error:

threads.rs:7:25: 7:39 error: use of undeclared type name `thread::scoped`
threads.rs:7     let mut guards: Vec<thread::scoped> = Vec::with_capacity(3);
                                 ^~~~~~~~~~~~~~
error: aborting due to previous error

There also seem to be a number of other problems, including using vin within a closure. Also, I have no idea what move does, other than the fact that every example I've seen seems to use it.

Upvotes: 0

Views: 1700

Answers (2)

oli_obk
oli_obk

Reputation: 31283

Your error is due to the fact that thread::scoped is a function, not a type. What you want is a Vec<T> where T is the result type of the function. Rust has a neat feature that helps you here: It automatically detects the correct type of your variables in many situations. If you use

let mut guards = Vec::with_capacity(3);

the type of guards will be chosen when you use .push() the first time.


There also seem to be a number of other problems.

  1. you are accessing guards[i] in the first for loop, but the length of the guards vector is 0. Its capacity is 3, which means that you won't have any unnecessary allocations as long as the vector never contains more than 3 elements. use guards.push(x) instead of guards[i] = x.

  2. thread::scoped expects a Fn() -> T, so your closure can return an object. You get that object when you call .join(), so you don't need an answer-vector.

  3. vin is moved to the closure. Therefore in the second iteration of the loop that creates your guards, vin isn't available anymore to be moved to the "second" closure. Every loop iteration creates a new closure.

  4. i is moved to the closure. I have no idea what's going on there. But the solution is to let inval = vin[i]; outside the closure, and then use inval inside the closure. This also solves Point 3.

  5. vin is mutable. Yet you never mutate it. Don't bind variables mutably if you don't need to.

  6. vin is an array of f64. Therefore (vin[i] as f64) does nothing. Therefore you can simply use vin[i] directly.

  7. join moves out of the guard. Since you cannot move out of an array, your cannot index into an array of guards and join the element at the specified index. What you can do is loop over the elements of the array and join each guard.

    Basically this means: don't iterate over indices (for i in 1..3), but iterate over elements (for element in vector) whenever possible.


All of the above implemented:

use std::thread;
use std::old_io::timer;
use std::time::duration::Duration;

fn main() {
    let vin = vec![1.4f64, 1.2f64, 1.5f64];
    let mut guards = Vec::with_capacity(3);

    for inval in vin {
        guards.push(thread::scoped( move || {
            let ms = (1000.0f64 * inval) as i64;
            let d = Duration::milliseconds(ms);
            timer::sleep(d);
            println!("Waited {}", inval);
            10.0f64 + inval
        }));
    }

    for guard in guards {
        let answer = guard.join();
        println!("{}", answer);
    };
}

Upvotes: 5

swizard
swizard

Reputation: 2711

In supplement of Ker's answer: if you really need to mutate arrays within a thread, I suppose the most closest valid solution for your task will be something like this:

use std::thread::spawn;
use std::old_io::timer;
use std::sync::{Arc, Mutex};
use std::time::duration::Duration;

fn main() {
    let vin = Arc::new(vec![1.4f64, 1.2f64, 1.5f64]);
    let answers = Arc::new(Mutex::new(vec![0f64, 0f64, 0f64]));
    let mut workers = Vec::new();

    for i in 0..3 {
        let worker_vin = vin.clone();
        let worker_answers = answers.clone();
        let worker = spawn( move || {
            let ms = (1000.0f64 * worker_vin[i]) as i64;
            let d = Duration::milliseconds(ms);
            timer::sleep(d);
            println!("Waited {}", worker_vin[i]);
            let mut answers = worker_answers.lock().unwrap();
            answers[i] = 10.0f64 + (worker_vin[i] as f64);
        });
        workers.push(worker);
    }

    for worker in workers { worker.join().unwrap(); }
    for answer in answers.lock().unwrap().iter() {
        println!("{}", answer);
    }
}

In order to share vectors between several threads, I have to prove, that these vectors outlive all of my threads. I cannot use just Vec, because it will be destroyed at the end of main block, and another thread could live longer, possibly accessing freed memory. So I took Arc reference counter, which guarantees, that my vectors will be destroyed only when the counter downs to zero.

Arc allows me to share read-only data. In order to mutate answers array, I should use some synchronize tools, like Mutex. That is how Rust prevents me to make data races.

Upvotes: 1

Related Questions