I want to share (and change) a mutable Vec
across threads, in a scoped thread pool context, using scoped_thread_pool.
let ids_for_discard: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
let scoped_outcome = self.threadpool.scoped(|scope| {
// here I iterate through a Vec of objects...
for text_doc_hit_obj in &self.handling_framework.text_doc_hit_objs_for_processing {
// each thread will need its Arc clone...
let ids_for_discard = Arc::clone(&ids_for_discard);
scope.execute(move || {
// this (above) closure MUST return "()"... so
// I use an inner closure to capture errors efficiently, using the "?" operator several times
let inner_closure = move || -> Result<(), Box<dyn std::error::Error>> {
...
let id_string = text_doc_hit_obj._id.clone();
// let _ = ids_for_discard.lock()?.push(id_string); // fails to compile
let _ = ids_for_discard.lock().unwrap().push(id_string); // passes
... it also passes if I use a match
clause.
The error I get using the ?
operator above is:
error[E0515]: cannot return value referencing local data `ids_for_discard`
--> src\indexing_pool.rs:112:19
|
112 | ... let _ = ids_for_discard.lock()?.push(id_string);
| ----------------------^
| |
| returns a value referencing data owned by the current function
| `ids_for_discard` is borrowed here
Can anyone explain why ?
fails to compile here? I'm really struggling to understand. I wondered whether there might be a danger that an attempt is made to push
an Err
... but in the event that lock()
fails, shouldn't this immediately send control, with this Err
, out of the local closure?
NB this also fails:
let id_string = text_doc_hit_obj._id.clone();
let mut vec = ids_for_discard.lock()?;
vec.push(id_string);
Compiler: error[E0515]: cannot return value referencing local data 'ids_for_discard'
Again, if I replace ?
with unwrap()
, it passes.
I see that the signature of Mutex::lock()
is
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>>
... and LockResult
is pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
. Could it be that Box<dyn std::error::Error>>
is unable to capture this kind of error???
The PoisonError
does contain a MutexGuard
and with it a reference to the Arc<Mutex<..>>
which in your case is local to the function, so you can't return references to it. You can map_err
to exchange the error for something which doesn't contain a reference to any local variable:
use std::sync::Mutex;
fn err_on_mutex_poisoned() -> Result<(), Box<dyn std::error::Error>> {
let x = Mutex::new(0);
// x.lock()?; // doesn't work
x.lock().map_err(|_| "failed")?; // works
Ok(())
}