是否可以通过渠道发送闭包?
Is it possible to send closures via 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;
});
}
我遇到了一堆错误:
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`
好像FnOnce
不能发送,但我不明白为什么。
是的。您的代码存在一些问题。
首先,FnOnce
是一个trait,不能直接使用。特征必须是对具体类型的约束,或者是某种间接的背后。由于您要将闭包发送到其他地方,因此您需要 Box<FnOnce(...)>
.
其次,你不能使用 Box<FnOnce(...)>
因为,由于对象安全规则,你实际上不能通过间接 调用 一个 FnOnce
.
(顺便说一句,您也不想使用 FnOnce<...>
语法,这在技术上是不稳定的;请改用 FnOnce(...)
。)
要解决这个问题,您可以切换到 Fn
或 FnMut
或 使用尚未稳定的 FnBox
特性。我之所以走这条路,是因为它可能具有您想要的语义,并且很可能在不久的将来稳定下来。如果您对此感到不舒服,您将需要适当地修改您的闭包。
以下是我和 Manishearth(他指出我错过了 + Send
约束)的共同努力:
// 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);
}
接受的答案没有详细说明,但如果您不使用 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();
}
我想通过渠道发送闭包:
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;
});
}
我遇到了一堆错误:
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`
好像FnOnce
不能发送,但我不明白为什么。
是的。您的代码存在一些问题。
首先,FnOnce
是一个trait,不能直接使用。特征必须是对具体类型的约束,或者是某种间接的背后。由于您要将闭包发送到其他地方,因此您需要 Box<FnOnce(...)>
.
其次,你不能使用 Box<FnOnce(...)>
因为,由于对象安全规则,你实际上不能通过间接 调用 一个 FnOnce
.
(顺便说一句,您也不想使用 FnOnce<...>
语法,这在技术上是不稳定的;请改用 FnOnce(...)
。)
要解决这个问题,您可以切换到 Fn
或 FnMut
或 使用尚未稳定的 FnBox
特性。我之所以走这条路,是因为它可能具有您想要的语义,并且很可能在不久的将来稳定下来。如果您对此感到不舒服,您将需要适当地修改您的闭包。
以下是我和 Manishearth(他指出我错过了 + Send
约束)的共同努力:
// 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);
}
接受的答案没有详细说明,但如果您不使用 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();
}