vitaly
vitaly

Reputation: 2945

Multi-threaded communication with an external process in Rust

A rust newbie here.

I would like to launch an external long-running process and talk with it over pipes from multiple threads in Rust.

I am getting lifetime errors and can't figure the proper way to please the lifetimes checker. What are the ways to restructure this?

Consider the following example:

use std::process::{Command, Stdio, ChildStdin};
use std::sync::Mutex;
use std::io::{Write};
use std::thread;

struct Element {
    sink: Mutex<Option<ChildStdin>>
}

impl Element {
    fn launch_process(&self) {
        let child =
            Command::new("sed").args(&["s/foo/bar/g"])
                .stdin(Stdio::piped())
                .spawn()
                .unwrap();

        let mut sink = self.sink.lock().unwrap();
        *sink = child.stdin;
    }

    fn tx(&self, content: &[u8]) {
        let mut sink = self.sink.lock().unwrap();
        sink.as_mut().unwrap().write(content);
    }

    fn start_tx(&self) {
        thread::spawn( || {
            self.tx(b"foo fighters");
        });
    }
}

fn main() {
    let e = Element {
        sink: Mutex::new(None)
    };

    e.launch_process();
    e.start_tx();
}

If I remove the thread::spawn bit then everything works as expected. With thread::spawn in place, I get the error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:28:24
   |
28 |           thread::spawn( || {
   |  ________________________^
29 | |             self.tx(b"foo fighters");
30 | |         });
   | |_________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 27:5...
  --> src/main.rs:27:5
   |
27 | /     fn start_tx(&self) {
28 | |         thread::spawn( || {
29 | |             self.tx(b"foo fighters");
30 | |         });
31 | |     }
   | |_____^
   = note: ...so that the types are compatible:
           expected &&Element
              found &&Element
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src/main.rs:28:24: 30:10 self:&&Element]` will meet its required lifetime bounds
  --> src/main.rs:28:9
   |
28 |         thread::spawn( || {
   |         ^^^^^^^^^^^^^

error: aborting due to previous error

Upvotes: 3

Views: 1019

Answers (1)

Kornel
Kornel

Reputation: 100090

You can't pass &self (a temporary borrow) to a thread, because the thread may keep running after the reference is no longer valid.

For using data from threads you have only two options:

  • Give ownership (which is exclusive) of the object to the thread, i.e. use move || closure, and don't try to use that object afterwards from the main thread, or any other thread.

  • Wrap the object in Arc to get shared thread-safe ownership, and send a clone to the thread (with Arc::clone it's cheap and the underlying data is shared).

When the compiler says that you need a "static lifetime", ignore that. For all practical purposes, it means "references are not allowed".

Upvotes: 1

Related Questions