-1

I have this code:

fn main() {
    let mut total: i64 = 0;
    let pool = ThreadPool::with_name("search worker".to_owned(), num_cpus::get());
    let items = getItems(); // returns a vector of sorts
    let client = blocking::Client::new();
    for item in items {
        let t_client = client.clone(); // must clone here for some reason
        let mut took: i64 = 0;
        pool.execute(move || {
            took = worker(t_client);
            total += took;
        });
    }
    println!("{}", total);
}

fn worker(c: blocking::Client) -> i64 {
    // do some stuff and return a value
    // for sake of an example, return 25

    25
}

I had to use the move clause in the call to execute.

The problem is that the value of the total variable remains zero. It looks like it's being duplicated inside that loop and the local variable does not get modified at all.

I do get a warning on took = worker(t_client); and on total_took += took; which is that those variables aren't never read.

I ended up "solving" this by making that variable static and using unsafe, but that's not a solution. Also, eventually I need to get more results from that worker function and if I put a reference to a variable, it tell me that I can't borrow more than once, which I don't really understand.

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
E.T
  • 1,095
  • 1
  • 10
  • 19
  • It's hard to answer your question because it doesn't include a [MRE]. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) if possible, otherwise in a brand new Cargo project, then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Jan 08 '21 at 18:58
  • Please [edit] your question and paste the exact and entire error that you're getting — that will help us to understand what the problem is so we can help best. Sometimes trying to interpret an error message is tricky and it's actually a different part of the error message that's important. Please use the message from running the compiler directly, not the message produced by an IDE, which might be trying to interpret the error for you. – Shepmaster Jan 08 '21 at 18:58
  • By the way, idiomatic Rust uses `snake_case` for variables, methods, macros, fields and modules; `UpperCamelCase` for types and enum variants; and `SCREAMING_SNAKE_CASE` for statics and constants. – Shepmaster Jan 08 '21 at 19:01

2 Answers2

2

total is of type i64 which implements the Copy trait so it's implicitly copied on moves. If you want to share some mutable value T across multiple threads you need to wrap it in an Arc<Mutex<T>> (Arc docs & Mutex docs), or in this particular case, since you're using an i64, you can use an AtomicI64 instead.

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
  • 1
    I have typed out a similar answer, just with the fixes/examples. Now it feels odd posting it :) – vallentin Jan 08 '21 at 19:00
  • 1
    See also [How can I pass a reference to a stack variable to a thread?](https://stackoverflow.com/q/32750829/155423); [How do I share a mutable object between threads using Arc?](https://stackoverflow.com/q/31373255/155423); – Shepmaster Jan 08 '21 at 19:01
  • That makes sense. I figured that the variable was getting copied (to prevent race conditions I assume), but I didn't know about the Arc<> thing. I'm going to give that a shot. Thanks for the link too, I'll read that. – E.T Jan 08 '21 at 19:07
0

Based on @pretzelhammer's answer I write a small snippet on how I got it to work.

let total = Arc::new(AtomicI64::new(0));
...
for ... {
  let total_c = total.clone();
  pool.execute(move || {
      total_c.fetch_add(es_helper::run_search(t_client, parsed), Ordering::Relaxed);
  });
}
E.T
  • 1,095
  • 1
  • 10
  • 19