我可以传播 Send
函数参数的特征为其返回类型,因此返回类型为 impl Send
当且仅当参数是?
详细信息:
异步函数有一个很好的特性。它返回了Future
自动为Send
如果可以的话。在以下示例中,异步函数将创建 Future
即Send
,如果函数的输入是 Send
.
struct MyStruct;
impl MyStruct {
// This async fn returns an `impl Future<Output=T> + Send` if `T` is Send.
// Otherwise, it returns an `impl Future<Output=T>` without `Send`.
async fn func<T>(&self, t: T) -> T {
t
}
}
fn assert_is_send(_v: impl Send) {}
fn main() {
// This works
assert_is_send(MyStruct.func(4u64));
// And the following correctly fails
assert_is_send(MyStruct.func(std::rc::Rc::new(4u64)));
}
现在,我想将这样的函数移至特征中,这需要使用 async-trait (这是一些代码生成器,可以有效地将我的 async fn
编写为返回 Pin<Box<dyn Future>>
的函数)或手动执行类似的操作。有没有办法以保留这种自动发送行为的方式编写此行为,其中返回 Future
制作Send
如果T
是 Send
?以下示例将其实现为两个单独的函数:
use std::pin::Pin;
use std::future::Future;
struct MyStruct;
impl MyStruct {
fn func_send<T: 'static + Send>(&self, t: T) -> Pin<Box<dyn Future<Output = T> + Send>> {
Box::pin(async{t})
}
fn func_not_send<T: 'static>(&self, t: T) -> Pin<Box<dyn Future<Output = T>>> {
Box::pin(async{t})
}
}
fn assert_is_send(_v: impl Send) {}
fn main() {
// This works
assert_is_send(MyStruct.func_send(4u64));
// And the following correctly fails
// assert_is_send(MyStruct.func(std::rc::Rc::new(4u64)));
}
但实际上,我不想让他们分开。我希望它们成为一个类似于 async fn
的函数自动执行。类似的东西
use std::pin::Pin;
use std::future::Future;
struct MyStruct;
impl MyStruct {
fn func<T: 'static + ?Send>(&self, t: T) -> Pin<Box<dyn Future<Output = T> + ?Send>> {
Box::pin(async{t})
}
}
fn assert_is_send(_v: impl Send) {}
fn main() {
// This should
assert_is_send(MyStruct.func(4u64));
// And this should fail
assert_is_send(MyStruct.func(std::rc::Rc::new(4u64)));
}
Rust 中可能有这样的事情吗?我可以手动编写异步特征魔法并修改它,而不是使用异步特征箱(如果这是使其工作的一种方法)。
我有一些想法,但尚未真正实现:
- 使用 min-specialization 来专门研究
Send
?但该功能似乎不会很快稳定下来,所以可能不是最好的选择。 - 返回自定义
MyFuture
输入而不只是impl Future
不知何故impl Send for MyFuture where T: Send
?不过可能会很困难,因为我必须能够命名它Future
和async
代码通常会生成impl Future
无法命名的类型。 - 编写一个过程宏,添加
+ Send
如果识别出输入类型为Send
,则返回返回类型。实际上,过程宏可以检测某种类型是否实现Send
?我的猜测是这是不可能的,因为它们只适用于 token 流。
最佳答案
(2)是唯一可行的方法。
有两种方法可以使其发挥作用:
- 手动书写 future ,无需
async
的帮助和.await
。但这意味着手动书写 future :
enum ConditionalSendFut<T> {
Start { t: T },
Done,
}
impl<T> Unpin for ConditionalSendFut<T> {}
impl<T> Future for ConditionalSendFut<T> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, _context: &mut Context<'_>) -> Poll<Self::Output> {
match &mut *self {
Self::Start { .. } => {
let t = match std::mem::replace(&mut *self, Self::Done) {
Self::Start { t } => t,
_ => unreachable!(),
};
Poll::Ready(t)
}
Self::Done => Poll::Pending,
}
}
}
struct MyStruct;
impl MyStruct {
fn func<T: 'static>(&self, t: T) -> ConditionalSendFut<T> {
ConditionalSendFut::Start { t }
}
}
- 存储
Pin<Box<dyn Future<Output = T>>>
并有条件地实现Send
关于 future 。但这需要unsafe
代码并手动确保您不持有其他非Send
类型.await
点:
struct ConditionalSendFut<T>(Pin<Box<dyn Future<Output = T>>>);
// SAFETY: The only non-`Send` type we're holding across an `.await`
// point is `T`.
unsafe impl<T: Send> Send for ConditionalSendFut<T> {}
impl<T> Future for ConditionalSendFut<T> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
self.0.as_mut().poll(context)
}
}
struct MyStruct;
impl MyStruct {
fn func<T: 'static>(&self, t: T) -> ConditionalSendFut<T> {
ConditionalSendFut(Box::pin(async { t }))
}
}
(1) 无法使用特征,因为每个 impl 都有不同的 future 。这样我们就只剩下(2)了。我不会推荐它,但这是可能的。
当特征中的异步fns稳定时,很可能会有一种机制(目前讨论的是有条件地实现它们并在使用站点上使用边界来要求它们),但目前还没有这样的机制事情,即使是在特征中每晚实现异步 fns 。
关于rust - 如果参数是 Send,则返回 Future Send,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74298792/