2

I would like to send commands (closures) to different threads, with the closure capturing a non-Sync variable (therefore I cannot "share" the closure with an Arc, as explained in Can you clone a closure?).

The closure only captures elements which implement Clone, so I would feel that the closure could derive Clone as well.

#![feature(fnbox)]

use std::boxed::FnBox;
use std::sync::mpsc::{self, Sender};
use std::thread;

type Command = Box<FnBox(&mut SenderWrapper) + Send>;

struct SenderWrapper {
    tx: Option<Sender<u64>>,
}
impl SenderWrapper {
    fn new() -> SenderWrapper {
        SenderWrapper { tx: None }
    }
}

fn main() {
    let (responses_tx, responses_rx) = mpsc::channel();
    let closure: Command = Box::new(move |snd: &mut SenderWrapper| {
        snd.tx = Some(responses_tx); // Captures tx, which is not Sync but is Clone
    });

    let mut commands = Vec::new();
    for i in 0..2i32 {
        let (commands_tx, commands_rx) = mpsc::channel();
        commands.push(commands_tx);
        thread::spawn(move || {
            let mut wrapper = SenderWrapper::new();
            let command: Command = commands_rx.recv().unwrap();
            command.call_box((&mut wrapper,));
            // Continue ...
        });
    }

    for tx in commands.iter() {
        commands[0].send(closure.clone()).unwrap(); // How can I make this clone() work?
    }
    // use answers ...
}
error[E0599]: no method named `clone` found for type `std::boxed::Box<for<'r> std::boxed::FnBox<(&'r mut SenderWrapper,), Output=()> + 'static>` in the current scope
  --> src/main.rs:40:34
   |
40 |         commands[0].send(closure.clone()).unwrap();
   |                                  ^^^^^
   |
   = note: the method `clone` exists but the following trait bounds were not satisfied:
           `std::boxed::Box<for<'r> std::boxed::FnBox<(&'r mut SenderWrapper,), Output=()>> : std::clone::Clone`

Is there any way in the current syntax where we can implement / derive traits for closures?

A dirty workaround for this would be to define (by hand) a structure containing the required environment, implement Clone, and define the FnOnce or Invoke trait.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vaelden
  • 2,089
  • 1
  • 12
  • 14

1 Answers1

2

Rust 1.35

Box<dyn FnOnce> is stable. Your code now works in stable Rust if you change it to only box the closure after cloning it:

use std::sync::mpsc::{self, Sender};
use std::thread;

type Command = Box<FnOnce(&mut SenderWrapper) + Send>;

struct SenderWrapper {
    tx: Option<Sender<u64>>,
}
impl SenderWrapper {
    fn new() -> SenderWrapper {
        SenderWrapper { tx: None }
    }
}

fn main() {
    let (responses_tx, responses_rx) = mpsc::channel();
    let closure = move |snd: &mut SenderWrapper| {
        snd.tx = Some(responses_tx); // Captures tx, which is not Sync but is Clone
    };

    let mut commands = Vec::new();
    for i in 0..2i32 {
        let (commands_tx, commands_rx) = mpsc::channel();
        commands.push(commands_tx);
        thread::spawn(move || {
            let mut wrapper = SenderWrapper::new();
            let command: Command = commands_rx.recv().unwrap();
            command(&mut wrapper);
            // Continue ...
        });
    }

    for tx in commands.iter() {
        tx.send(Box::new(closure.clone())).unwrap(); // How can I make this clone() work?
    }
    // use answers ...
}

See also:

Rust 1.26

Closures now implement Clone

Prior

This doesn't answer your direct question, but is it feasible to just clone your variables before capturing them in the closure?

for tx in commands.iter() {
    let my_resp_tx = responses_tx.clone();
    let closure = Box::new(move |snd: &mut SenderWrapper| {
        snd.tx = Some(my_resp_tx);
    });
    commands[0].send(closure).unwrap();
}

You can even extract this logic into a "factory" function.


Let's take a deeper look. First, we recognize that Box<FnBox> is a trait object, and cloning those is kind of difficult. Following the answers in How to clone a struct storing a boxed trait object?, and using a smaller case, we end up with:

#![feature(fnbox)]

use std::boxed::FnBox;

type Command = Box<MyFnBox<Output = ()> + Send>;

trait MyFnBox: FnBox(&mut u8) + CloneMyFnBox {}

trait CloneMyFnBox {
    fn clone_boxed_trait_object(&self) -> Box<MyFnBox<Output = ()> + Send>;
}

impl<T> CloneMyFnBox for T
where
    T: 'static + MyFnBox + Clone + Send,
{
    fn clone_boxed_trait_object(&self) -> Box<MyFnBox<Output = ()> + Send> {
        Box::new(self.clone())
    }
}

impl Clone for Box<MyFnBox<Output = ()> + Send> {
    fn clone(&self) -> Box<MyFnBox<Output = ()> + Send> {
        self.clone_boxed_trait_object()
    }
}

fn create() -> Command {
    unimplemented!()
}

fn main() {
    let c = create();
    c.clone();
}

Of note is that we had to introduce a trait to implement cloning and another trait to combine the cloning trait with FnBox.

Then it's "just" a matter of implementing MyFnBox for all implementors of FnBox and enabling another nightly feature: #![clone_closures] (slated for stabilization in Rust 1.28):

#![feature(fnbox)]
#![feature(clone_closures)]

use std::boxed::FnBox;
use std::sync::mpsc::{self, Sender};
use std::thread;

struct SenderWrapper {
    tx: Option<Sender<u64>>,
}
impl SenderWrapper {
    fn new() -> SenderWrapper {
        SenderWrapper { tx: None }
    }
}

type Command = Box<MyFnBox<Output = ()> + Send>;

trait MyFnBox: FnBox(&mut SenderWrapper) + CloneMyFnBox {}

impl<T> MyFnBox for T
where
    T: 'static + FnBox(&mut SenderWrapper) + Clone + Send,
{
}

trait CloneMyFnBox {
    fn clone_boxed_trait_object(&self) -> Box<MyFnBox<Output = ()> + Send>;
}

impl<T> CloneMyFnBox for T
where
    T: 'static + MyFnBox + Clone + Send,
{
    fn clone_boxed_trait_object(&self) -> Box<MyFnBox<Output = ()> + Send> {
        Box::new(self.clone())
    }
}

impl Clone for Box<MyFnBox<Output = ()> + Send> {
    fn clone(&self) -> Box<MyFnBox<Output = ()> + Send> {
        self.clone_boxed_trait_object()
    }
}

fn main() {
    let (responses_tx, responses_rx) = mpsc::channel();
    let closure: Command = Box::new(move |snd: &mut SenderWrapper| {
        snd.tx = Some(responses_tx);
    });

    let mut commands = Vec::new();
    for i in 0..2i32 {
        let (commands_tx, commands_rx) = mpsc::channel();
        commands.push(commands_tx);
        thread::spawn(move || {
            let mut wrapper = SenderWrapper::new();
            let command: Command = commands_rx.recv().unwrap();
            command.call_box((&mut wrapper,));
            // Continue ...
        });
    }

    for tx in commands.iter() {
        commands[0].send(closure.clone()).unwrap(); // How can I make this clone() work?
    }
    // use answers ...
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    In the simple example I provided, it would indeed be possible. However, in my real use case, the command comes from another part of the code, which is not supposed to know how many times the closure will be cloned. – Vaelden Jan 18 '15 at 17:27
  • Would you be able to extract the closure generation into a function and then call that function for each task? – Shepmaster Jan 18 '15 at 20:25
  • 2
    Yes actually it should be possible to do a function or a closure that would be a "sendable closure" factory. Thanks for the idea! That's a shame we can't clone() closures directly though ... – Vaelden Jan 18 '15 at 21:34