How to pass a function as parameter for thread?

I’m a new rustacean and I try to pass a function as argument in another function in order to create threads with the function pass as argument.

Here the code :

use std::os::unix::net::{UnixListener, UnixStream};
use std::thread;
use std::io::Read;
use anyhow::Context;

pub struct SocketServer {
    path: String,
    listener: UnixListener,
}

impl SocketServer {
    pub fn new(path: &str) -> anyhow::Result<SocketServer>{

        if std::fs::metadata(path).is_ok() {
            println!("A socket is already present. Deleting...");
            std::fs::remove_file(path).with_context(|| {
                format!("could not delete previous socket at {:?}", path)
            })?;
        }

        let socket_listener =
            UnixListener::bind(path).context("Could not create the unix socket")?;

        let path = path.to_string();

        Ok(SocketServer{ path, listener: socket_listener })
    }

    pub fn start(&self, f: &dyn Fn(UnixStream)) -> anyhow::Result<()>{

        for stream in self.listener.incoming() {
            match stream {
                Ok(stream) => {
                    thread::spawn(||f(stream));
                }
                Err(err) => {break;}
            }
        }
        Ok(())
    }
}

pub fn handle_stream(mut unix_stream: UnixStream) -> anyhow::Result<()> {
    let mut message = String::new();
    unix_stream
        .read_to_string(&mut message)
        .context("Failed at reading the unix stream")?;

    println!("We received this message: {}", message);

    Ok(())
}

Here the error I get when in try to compile :

error[E0277]: `dyn Fn(UnixStream)` cannot be shared between threads safely
   --> src/socket.rs:34:35
    |
34  |                     thread::spawn(||f(stream));
    |                     ------------- ^^^^^^^^^^^ `dyn Fn(UnixStream)` cannot be shared between threads safely
    |                     |
    |                     required by a bound introduced by this call
    |
    = help: the trait `Sync` is not implemented for `dyn Fn(UnixStream)`
    = note: required for `&dyn Fn(UnixStream)` to implement `Send`

I got some information in the Rust book but I still don’t understand wich function need to implement what.
Can you give me some hints? (Advices on other part are welcome too )

I tried to remove closure but it goes to another error :

error[E0277]: expected a `FnOnce<()>` closure, found `()`
   --> src/socket.rs:34:35
    |
34  |                     thread::spawn(f(stream));
    |                     ------------- ^^^^^^^^^ expected an `FnOnce<()>` closure, found `()`
    |                     |
    |                     required by a bound introduced by this call
    |
    = help: the trait `FnOnce<()>` is not implemented for `()`
    = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }`
note: required by a bound in `spawn`
   --> /rust/lib/rustlib/src/rust/library/std/src/thread/mod.rs:661:8
    |
661 |     F: FnOnce() -> T,
    |        ^^^^^^^^^^^^^ required by this bound in `spawn`

>Solution :

Is there a reason you need a dyn Fn?
The easiest is probably to accept a impl Fn(UnixStream) + Send + Clone + 'static instead

pub fn start<F>(&self, f: F) -> anyhow::Result<()>
where
    F: Fn(UnixStream) + Send + Clone + 'static,
{
    for stream in self.listener.incoming() {
        let f = f.clone();
        match stream {
            Ok(stream) => {
                thread::spawn(move || f(stream));
            }
            Err(err) => break,
        }
    }
    Ok(())
}

Leave a Reply