How to send a closure with captures through the mpsc channel in rust?

I would like to implement an executor that can execute the packaged task in its queue. Firstly,I hope to make different types of functions into one type using a cloure, then send the closure to channel, receive it in thread and execute it. The codes are below :

use std::thread;
use std::sync::mpsc;
macro_rules! packed_task {    
    ($f:ident, $($arg:expr),*) => {move ||{
        $f($($arg,)*)
    }};
}
macro_rules! packed_method_task {
    ($f:ident,$ins:ident, $($arg:expr),*) => {move ||{
        $ins.$f($($arg,)*);
    }};
    ($f:ident,$ins:ident $($arg:expr),*) => {move ||{
         $ins.$f($($arg,)*);
    }};
}
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
    fn area1(&self, w:u32, h:u32) -> u32 {
        w*h
    }
}
fn invoke_1(a:i32, b:i32, c:i32)->i32{
    let fc = |x,y,z| x+y+z + 1;
    return packed_task!(fc, a, b,c)();
}

fn main() {
    println!("{}", invoke_1(1,2,3));
    let rect1 = Rectangle { width: 30, height: 50 };
    let b = packed_method_task!(area1, rect1, 60, 90);
    let (tx, rx) = mpsc::channel();
    
    let handle= thread::spawn(move || {
        let _received = rx.recv().unwrap();
        _received();
    });
    
    tx.send(b).unwrap();
    handle.join();
}

I wrote codes up, but I can't compile it successfully!
I would like to send a closure with captures through the mpsc channel, how can I achieve that?

1 Like

This is not the way it works, sadly, since different closures (even with exact same code) have different types. See this modification of your code (playground):

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    let b = packed_method_task!(area1, rect1, 60, 90);
    let c = packed_method_task!(area1, rect1, 60, 90);
    let (tx, rx) = mpsc::channel();
    
    tx.send(b).unwrap();
    tx.send(c).unwrap();
    
    let handle= thread::spawn(move || {
        let _received = rx.recv().unwrap();
        _received();
    });
    handle.join();
}

It errors due to b and c having different types. Note that if you send only b or only c, it works - in your original code the error essentially tells you the following: "you're receiving something, I don't know yet what it is, so you can't call it"; by moving send(b) above thread::spawn, you allow type inference to kick in and to infer the exact type.

You can do this, but you'll need to send a Box<dyn Fn()>. The compiler will insert the necessary information for you to still be able to call it after you've received it. This adds a little bit of overhead, but is what you need to do if you want to send arbitrary closures. playground

fn main() {
    println!("{}", invoke_1(1,2,3));
    let rect1 = Rectangle { width: 30, height: 50 };
    let b = packed_method_task!(area1, rect1, 60, 90);
    let c = packed_method_task!(area1, rect1, 60, 90);
    // specify the type here
    let (tx, rx) = mpsc::channel::<Box<dyn Fn() + Send>>();
    
    // Box values before sending them
    tx.send(Box::new(b)).unwrap();
    tx.send(Box::new(c)).unwrap();
    
    let handle= thread::spawn(move || {
        let _received = rx.recv().unwrap();
        _received();
    });
    
    handle.join();
}
3 Likes