Can I send gateways over channels?

I would like to send a closure on the channels:

use std::thread; use std::sync::mpsc; #[derive(Debug)] struct Test { s1: String, s2: String, } fn main() { let t = Test { s1: "Hello".to_string(), s2: "Hello".to_string(), }; let (tx, rx) = mpsc::channel::<FnOnce(&mut Test)>(); thread::spawn(move || { let mut test = t; let f = rx.recv().unwrap(); f(&mut test); println!("{:?}", test); }); tx.send(move |t: &mut Test| { let s = "test".to_string(); t.s1 = s; }); } 

( playground )

I get a bunch of errors:

 error[E0277]: the trait bound `for<'r> std::ops::FnOnce(&'r mut Test): std::marker::Sized` is not satisfied --> src/main.rs:15:20 | 15 | let (tx, rx) = mpsc::channel::<FnOnce(&mut Test)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `for<'r> std::ops::FnOnce(&'r mut Test)` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `for<'r> std::ops::FnOnce(&'r mut Test)` = note: required by `std::sync::mpsc::channel` error[E0277]: the trait bound `for<'r> std::ops::FnOnce(&'r mut Test): std::marker::Sized` is not satisfied --> src/main.rs:15:20 | 15 | let (tx, rx) = mpsc::channel::<FnOnce(&mut Test)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `for<'r> std::ops::FnOnce(&'r mut Test)` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `for<'r> std::ops::FnOnce(&'r mut Test)` = note: required by `std::sync::mpsc::Sender` error[E0599]: no method named `recv` found for type `std::sync::mpsc::Receiver<for<'r> std::ops::FnOnce(&'r mut Test)>` in the current scope --> src/main.rs:18:20 | 18 | let f = rx.recv().unwrap(); | ^^^^ | = note: the method `recv` exists but the following trait bounds were not satisfied: `for<'r> std::ops::FnOnce(&'r mut Test) : std::marker::Sized` error[E0599]: no method named `send` found for type `std::sync::mpsc::Sender<for<'r> std::ops::FnOnce(&'r mut Test)>` in the current scope --> src/main.rs:22:8 | 22 | tx.send(move |t: &mut Test| { | ^^^^ | = note: the method `send` exists but the following trait bounds were not satisfied: `for<'r> std::ops::FnOnce(&'r mut Test) : std::marker::Sized` 

It seems that FnOnce not sending, but I do not understand why.

+8
rust
source share
2 answers

Yes. There are several problems with your code.

First of all, FnOnce is a sign, so you cannot use it directly. Traits should be either a restriction on a particular type, or for any indirectness. Since you are sending the closure to another place, you need something like Box<FnOnce(...)> .

Secondly, you cannot use Box<FnOnce(...)> because, due to object security rules, you cannot actually call FnOnce through indirect binding.

(As an aside, you also don't want to use the FnOnce<...> syntax, which is technically unstable, use FnOnce(...) instead.)

To solve this problem, you can switch to Fn or FnMut or use the not yet stable FnBox feature. I took this path on the grounds that it probably has the semantics you want and is likely to be stabilized in the near future. If you are not comfortable, you need to change your closure accordingly.

The following is a collaborative effort between me and the Manisher (who indicated that I missed the + Send constraint):

 // NOTE: Requires a nightly compiler, as of Rust 1.0. #![feature(core)] use std::boxed::FnBox; use std::thread; use std::sync::mpsc; #[derive(Debug)] struct Test { s1: String, s2: String, } type ClosureType = Box<FnBox(&mut Test) + Send>; fn main() { let t = Test { s1: "Hello".to_string(), s2: "Hello".to_string() }; let (tx, rx) = mpsc::channel::<ClosureType>(); thread::spawn(move || { let mut test = t; let f = rx.recv().unwrap(); f.call_box((&mut test,)); println!("{:?}", test); }); tx.send(Box::new(move |t: &mut Test| { let s = "test".to_string(); t.s1 = s; })).unwrap(); // To give the output time to show up: thread::sleep_ms(100); } 
+9
source share

The accepted answer did not go into details, but you can send circuits to threads through channels, even on a stable one, if you do not use FnOnce :

 use std::thread; use std::sync::mpsc; struct RawFunc { data: Box<Fn() + Send + 'static>, } impl RawFunc { fn new<T>(data: T) -> RawFunc where T: Fn() + Send + 'static, { return RawFunc { data: Box::new(data), }; } fn invoke(self) { (self.data)() } } fn main() { // Local let x = RawFunc::new(move || { println!("Hello world"); }); x.invoke(); // Via channel let (sx, rx) = mpsc::channel::<RawFunc>(); sx.send(RawFunc::new(move || { println!("Hello world 2"); })).unwrap(); let output = rx.recv().unwrap(); output.invoke(); // In a thread let guard = thread::spawn(move || { let output = rx.recv().unwrap(); output.invoke(); }); sx.send(RawFunc::new(move || { println!("Hello world 3!"); })).unwrap(); guard.join().unwrap(); // Passing arbitrary data to a thread let (sx, rx) = mpsc::channel::<RawFunc>(); let guard = thread::spawn(move || { let output = rx.recv().unwrap(); output.invoke(); }); let foo = String::from("Hello World 4"); sx.send(RawFunc::new(move || { println!("Some moved data: {:?}", foo); })).unwrap(); guard.join().unwrap(); } 
+1
source share

All Articles