rust - 如果参数是 Send,则返回 Future Send

标签 rust async-await traits rust-futures

我可以传播 Send函数参数的特征为其返回类型,因此返回类型为 impl Send当且仅当参数是?

详细信息:

异步函数有一个很好的特性。它返回了Future自动为Send如果可以的话。在以下示例中,异步函数将创建 FutureSend ,如果函数的输入是 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)));
}

playground

现在,我想将这样的函数移至特征中,这需要使用 async-trait (这是一些代码生成器,可以有效地将我的 async fn 编写为返回 Pin<Box<dyn Future>> 的函数)或手动执行类似的操作。有没有办法以保留这种自动发送行为的方式编写此行为,其中返回 Future制作Send如果TSend ?以下示例将其实现为两个单独的函数:

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)));
}

playground

但实际上,我不想让他们分开。我希望它们成为一个类似于 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 ?不过可能会很困难,因为我必须能够命名它 Futureasync代码通常会生成 impl Future无法命名的类型。
  • 编写一个过程宏,添加 + Send如果识别出输入类型为Send,则返回返回类型。实际上,过程宏可以检测某种类型是否实现 Send ?我的猜测是这是不可能的,因为它们只适用于 token 流。

最佳答案

(2)是唯一可行的方法。

有两种方法可以使其发挥作用:

  1. 手动书写 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 }
    }
}

Playground .

  • 存储 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 }))
        }
    }
    

    Playground .

    (1) 无法使用特征,因为每个 impl 都有不同的 future 。这样我们就只剩下(2)了。我不会推荐它,但这是可能的。

    当特征中的异步fns稳定时,很可能会有一种机制(目前讨论的是有条件地实现它们并在使用站点上使用边界来要求它们),但目前还没有这样的机制事情,即使是在特征中每晚实现异步 fns 。

    关于rust - 如果参数是 Send,则返回 Future Send,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74298792/

    相关文章:

    c# - 如何访问调用异步方法时可用的变量?

    generics - 如何为任何元素序列实现特征?

    Scala 中间早期初始化器

    rust - 拆分字符串并返回 Vec<String>

    rust - 访问向量时避免可选检查

    rust - 读取一级目录结构

    rust - 无法为实现我拥有的特征的所有类型实现我不拥有的特征

    rust - 我可以将 `impl Future` 存储为具体类型吗?

    c# - 使用 Async 避免重复代码

    c# - 异步 - 等待 - 预期线程