Error Handling on Mutex


#1

Hi
I’m trying to add better Error Handling to a method, but get a “borrowed value does not live long enough” error.
Why does my old version works:

fn a(child: Arc<Mutex<Option<Child>>>) {
    child.lock().unwrap().get_or_insert(
        Command::new("echo")
        .spawn()
        .expect("Couldn't run"));
}

but the version with error propagation not?

fn a(child: Arc<Mutex<Option<Child>>>) ->  Result<String, Box<std::error::Error>>  {
    child.lock()?.get_or_insert(
        Command::new("echo")
        .spawn()?);

    Ok("Worked".to_string())
}

How can I fix this error?
Thanks, trembel


#2

Could you attach error message? Both error for locking mutex, and spawning process implements std::error::Error, and Box<T> implements From<T>, so it seems like it should work. My guess is, that your error type is Box<dyn std::error::Error>, not boxed concrete type, so it possibly may not be automaticaly converted with ?. If this is a case, possibly mapping error via Box::new may do the job, but its more or less blind shot (I don’t use Box<dyn Trait> much), error message would help with reasoning.


#3

This is what my error message looks like:

error[E0597]: `child` does not live long enough
   --> src/main.rs:202:9
    |
202 |         child.lock()?.get_or_insert(
    |         ^^^^^ borrowed value does not live long enough
...
258 | }
    | - borrowed value only lives until here
    |
    = note: borrowed value must be valid for the static lifetime...

#4

If I provoke a mismatched types error on the calling function, I get the following:

error[E0308]: mismatched types
   --> src/main.rs:410:5
    |
410 | /     match message.as_ref() {
...   |
413 | |         "_" => a(child.clone()) 
414 | |     }
    | |_____^ expected (), found enum `std::result::Result`
    |
    = note: expected type `()`
               found type `std::result::Result<std::string::String, std::boxed::Box<dyn std::error::Error>>`

#5

This error occurs rather because you assigning result of a to something (eg. you returning it as a result of function returning () like main). Problem seems to be with error risen by child.lock() which looks like having internal reference to guard, but documentation doesn’t show any lifetime about it. Could you create minimal example of not compiling code with only this particular error and link it via rust playground? Also if you are on nightly rust, try enabling nll - in this particular case I don’t think, that it would solve this problem, but it really improves lifetime related error messages.


#6

https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=02d61fda3bcab24fb6f1a014243ca4a9

I’m running it on nightly, but the error remained also when testing under stable in the playground.
If child.lock()? is replaced with child.lock().unwrap() the code works.


#7

The error from lock references child so can’t be used with try.
One way is to do something like .map_err(|_|"locking mutex failed")?. Typically when lock fails you don’t want to attempt to recover so just panic. (Hard to code to take account of poisoned Mutex.)


#8

Ok, thanks for your working solution. Since typically on a failed lock we want to panic, what are the cases that a lock can fail?


#9

lock() fails when the mutex is “poisoned”. This happens when another thread panics while holding the lock; Rust assumes that since the lock wasn’t released yet, the contents guarded by the mutex can be in an inconsistent state.

You’re still given access to the contents in the Err case, in case you know that in your case this can’t happen. (That’s also why you can’t pass that error along the call stack.)