How do i return a value from a thread func

i want to return Result<String,Box<dyn Error>> from a function that i pass to a thread spawn

pub fn launch_app(command: &String) -> Result<String, Box<dyn Error>> {
    let output = Command::new("sh").arg("-c").arg(command).output()?.stdout;

    let cmd_output = String::from_utf8(output)?;

    Ok(cmd_output)
}

What will be the best way to do so ?

it's not clear what problem you are facing, but I suppose it's a Send bound bound error?

try change dyn Error to dyn Error + Send:

pub fn launch_app(command: &String) -> Result<String, Box<dyn Error + Send>> {
    //...
}

Ok sorry for incomplete problem but i want to do :

  1. Pass Refrence to String(&)
  2. Get the Result
  3. Be able to use the Result
  4. and also how do i mutate passed String afterwards

spawn has the (simplified) signature: fn spawn(f: impl FnOnce() -> T) -> JoinHandle<T> - so it returns a "handle" you can join on to get the result, though since the thread may have panic-ed you need to also unwrap an additional layer of Result:

let handle = spawn(|| 3);
let result = handle.join();
assert_eq!(result, Ok(3));

let handle = spawn(|| panic!("oops"));
let result = handle.join();
assert_eq!(result, Err(Box::new("oops")));

So you'll probably just want to .expect() the first layer to "re-throw" any thread panic.

passing arguments directly to the thread entry point is not possible, but you can capture the arguments in a closure. note however, if you use thread::spawn(), (non-'static) references are not supported, because the thread runs in parallel with its parent thread, so the borrow checker will not be able to check the lifetime is valid.

two solutions:

  • clone the String and capture it by value:

    // make an owned copy of `command`
    let command: String = command.clone();
    // spawn the thread using a closure, note the `move` keyword
    let handle = thread::spawn(move || launch_app(&command));
    //...
    
    
  • or use thread::scope() instead of thread::spawn():

    thread::scope(|scope| {
        let handle = scope.spawn(|| launch_app(&command));
        //...
    });
    

as @simonbuchan said, you need to call .join() on the handle returned by spawn() (it's the same for std::thread::spawn() or scope.spawn()), just keep in mind that join() will wrap the return value with another layer of Result, so in your example, you get Result<Result<String, _>, _>. then you just need to handle the error however you like.

how's this question different from 2? can you be more specific?

the argument is like a local variable to the function, if you need to mutate it, you just declare it mut:

pub fn launch_app(command: &mut String) -> Result<String, Box<dyn Error + Send>> {
    //...
}

note, when you change the signature from &String to &mut String, the callsite needs to be changed accordingly, but the compiler will tell you if the type mismatched.

2 Likes

Just watch out for the fact that std::thread::scope() will block until the spawned threads all exit, so you can't join after the scope.


edit: not sure what I was thinking, actually, of course you can join after the scope closes. join just waits for the exit so if you await it after the scope exit it will immediately complete. So it works, but it's pretty useless!

4 Likes

thank you for the clone method it is what i needed and also i had no idea about thread::scope thanks also for that. I got the solution to my problem and sorry for my english(not my native lang)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.