 # Struggling with a method doing a computation in threads

Hi, I just started to learn Rust. For my toy project I've picked to write a linear algebra library. I would like to run some computations in parallel but I've ran into some troubles.

• Will different threads run on different cores or on one? In other words, just using threads will I have true parallelism or just a concurrency?
• My code doesn't compile. I've made a toy snippet just for this post that aims to compute an inner product of two vectors but with a separate thread for every term in the product.
``````use std::sync::{Arc, Mutex};

struct Vector {
_data: Arc<Mutex<Vec<f64>>>,
pub m: usize,
}

impl Vector {
fn new(d: Vec<f64>) -> Vector {
let m = d.len();
Vector {
_data: Arc::new(Mutex::new(d)),
m: m,
}
}

fn inner(&self, v: &Vector) -> f64 {
assert!(self.m == v.m);
let mut val = 0.;
let val_ref = Arc::new(Mutex::new(&val));

for i in 0..self.m {
let u_data = self._data.clone();
let v_data = v._data.clone();
let val_inner = val_ref.clone();
let u_loc = u_data.lock().unwrap();
let v_loc = v_data.lock().unwrap();
let mut val_loc = val_inner.lock().unwrap();
**val_loc += u_loc[i] * v_loc[i];
}));
}
}
val
}
}

fn main() {
let v = Vector::new(vec![1., 2.]);
let u = Vector::new(vec![-2., 2.]);
assert_eq!(u.inner(&v), 2.);
}``````

I get the following compile error:

``````error[E0594]: cannot assign to data in a dereference of `std::sync::MutexGuard<'_, &f64>`
--> src/main.rs:32:17
|
32 |                 **val_loc += u_loc[i] * v_loc[i];
|                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::sync::MutexGuard<'_, &f64>`

warning: variable does not need to be mutable
--> src/main.rs:20:13
|
20 |         let mut val = 0.;
|             ----^^^
|             |
|             help: remove this `mut`
|
= note: `#[warn(unused_mut)]` on by default

error[E0597]: `val` does not live long enough
--> src/main.rs:21:43
|
21 |         let val_ref = Arc::new(Mutex::new(&val));
|                                -----------^^^^-
|                                |          |
|                                |          borrowed value does not live long enough
|                                argument requires that `val` is borrowed for `'static`
...
39 |     }
|     - `val` dropped here while still borrowed

error: aborting due to 2 previous errors
``````

What is the right and idiomatic way to do this? Also why does the code compile, when I remove the return variable, i.e when every thread computes the product of two numbers and just drops it. I would expect the compiler complain about the lifetimes of the vectors. Does it use the "handle.join()" block to realize that the self and v object outlive the threads?

Rust uses the operating system’s native thread implementation, so it should run multi-core unless you are running in an unusual environment (like something embedded).

There are two related issues with your code, both caused by trying to hold an `&f64` inside `inner`’s `Mutex`:

• Because this is an immutable reference, you can’t store into its result. For that, you’d need an `&mut f64` instead.
• You can’t pass anything containing a temporary reference to a new thread, because the borrow checker can’t verify that the thread terminates before the reference becomes invalid. `val` is a local variable, and the compiler isn’t smart enough to verify that the child threads don’t store a reference to it somewhere.

If the `Mutex` holds the result value directly instead of a reference, it works:

``````    fn inner(&self, v: &Vector) -> f64 {
assert!(self.m == v.m);
let val = Arc::new(Mutex::new(0f64));

for i in 0..self.m {
let u_data = self._data.clone();
let v_data = v._data.clone();
let val_inner = val.clone();
let u_loc = u_data.lock().unwrap();
let v_loc = v_data.lock().unwrap();
let mut val_loc = val_inner.lock().unwrap();
*val_loc += u_loc[i] * v_loc[i];
}));
}
}
return *val.lock().unwrap();
}
``````
1 Like

Thank you very much. It works! I've tried this before, with one exception - instead of 'return *val.lock().unwrap();' I had only '*val.lock().unwrap()' and got a compilation error. Seems I missed some vital detail about the return keyword as I thought it's the same as just having the last block of code to be an expression.

I ran into that as well; it’s a subtle problem. Normally, when you end a code block with an expression, Rust doesn’t free the temporary values in that expression until the end of the containing statement; that mechanism caused some problems here.

This compiler message is what prompted me to try `return` instead:

``````
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
37 |         let x = *val.lock().unwrap(); x
|         ^^^^^^^                     ^^^
``````
1 Like