You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust异步构建器模式实现:无动态分派实现IntoFuture

为Rust构建器实现IntoFuture以避免动态分派

问题描述

我需要给配置请求的构建器实现Future能力,目前通过IntoFuture trait实现了直接await构建器的功能,但只能依赖Pin<Box<dyn Future>>做动态分派,希望能避免额外内存分配,使用具体的Future类型。

当前实现代码:

use std::time::Duration;
use tokio;

#[derive(Default)]
struct FooFluentBuilder {
    value: i32,
}

impl FooFluentBuilder {
    fn value(mut self, value: i32) -> Self {
        self.value = value;
        self
    }

    async fn send(self) -> Result<i32, &'static str> {
        tokio::time::sleep(Duration::from_millis(50)).await;
        Ok(self.value)
    }
}

impl std::future::IntoFuture for FooFluentBuilder {
    type Output = Result<i32, &'static str>;
    type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output>>>;

    fn into_future(self) -> Self::IntoFuture {
        Box::pin(self.send())
    }
}

#[tokio::main]
async fn main() {
    let _response = FooFluentBuilder::default().value(5).send().await;
    let _response = FooFluentBuilder::default().value(5).await;
}

核心问题是send()返回的是Rust异步函数生成的不透明匿名Future,无法直接作为IntoFuture的关联类型,只能用动态分派包装。


解决方案

有两种可靠方式可以避免动态分派,使用具体Future类型:

1. 手动实现命名Future结构体

将异步逻辑转换成手动实现的Future结构体,直接用这个结构体作为IntoFuture的关联类型,完全消除动态分派和堆分配。

修改后的代码:

use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::time::Sleep;

#[derive(Default)]
struct FooFluentBuilder {
    value: i32,
}

impl FooFluentBuilder {
    fn value(mut self, value: i32) -> Self {
        self.value = value;
        self
    }

    // 改为返回自定义Future类型
    fn send(self) -> FooSendFuture {
        FooSendFuture {
            inner: tokio::time::sleep(Duration::from_millis(50)),
            value: self.value,
        }
    }
}

// 自定义Future结构体,包含异步逻辑的状态
struct FooSendFuture {
    inner: Sleep,
    value: i32,
}

impl std::future::Future for FooSendFuture {
    type Output = Result<i32, &'static str>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.get_mut();
        // 委托给内部Sleep Future的poll方法
        match Pin::new(&mut this.inner).poll(cx) {
            Poll::Ready(_) => Poll::Ready(Ok(this.value)),
            Poll::Pending => Poll::Pending,
        }
    }
}

impl std::future::IntoFuture for FooFluentBuilder {
    type Output = Result<i32, &'static str>;
    // 使用具体的自定义Future类型
    type IntoFuture = FooSendFuture;

    fn into_future(self) -> Self::IntoFuture {
        self.send()
    }
}

#[tokio::main]
async fn main() {
    let _response = FooFluentBuilder::default().value(5).send().await;
    let _response = FooFluentBuilder::default().value(5).await;
}

这种方式性能最优,但需要手动编写poll方法,逻辑复杂时会增加代码量。

2. 使用type_alias_impl_trait稳定特性

从Rust 1.65开始,稳定版支持在类型别名中使用impl Trait,可以给异步函数返回的不透明Future定义别名,然后作为IntoFuture的关联类型使用。

首先在 crate 根启用特性:

#![feature(type_alias_impl_trait)]

修改后的实现代码:

#![feature(type_alias_impl_trait)]

use std::time::Duration;
use tokio;

#[derive(Default)]
struct FooFluentBuilder {
    value: i32,
}

impl FooFluentBuilder {
    fn value(mut self, value: i32) -> Self {
        self.value = value;
        self
    }

    async fn send(self) -> Result<i32, &'static str> {
        tokio::time::sleep(Duration::from_millis(50)).await;
        Ok(self.value)
    }

    // 定义不透明Future的类型别名
    type SendFuture = impl std::future::Future<Output = Result<i32, &'static str>>;
}

impl std::future::IntoFuture for FooFluentBuilder {
    type Output = Result<i32, &'static str>;
    // 使用类型别名作为关联类型
    type IntoFuture = FooFluentBuilder::SendFuture;

    fn into_future(self) -> Self::IntoFuture {
        self.send()
    }
}

#[tokio::main]
async fn main() {
    let _response = FooFluentBuilder::default().value(5).send().await;
    let _response = FooFluentBuilder::default().value(5).await;
}

这种方式保留了异步函数的简洁性,同时避免了动态分派和堆分配,是平衡代码简洁性和性能的最优选择。


内容的提问来源于stack exchange,提问作者Jinyoung Choi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 06:00:03