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

如何在安全Rust中实现FutureSource转Stream适配器?

为返回Future的类型实现Stream适配器的编译错误解决方案

问题背景

有一个语义上表现为流但未实现futures_util::Stream trait的类型FutureSource,它通过get_next方法反复返回产出下一项的Future。尝试为其实现适配器以当作Stream使用时,遇到了编译错误。

示例代码

use futures_util::Stream;
use std::{future::Future, pin::Pin, task::Poll};

struct FutureSource {/* omitted */}

impl FutureSource {
    pub fn get_next(&mut self) -> FutureString {
        FutureString {
            source: self,
            important_state: todo!(),
        }
    }
}

struct FutureString<'a> {
    source: &'a mut FutureSource,
    important_state: String,
}

impl<'a> Future for FutureString<'a> {
    type Output = String;

    fn poll(self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
        unimplemented!()
    }
}

struct StreamAdapter<'a> {
    future_source: &'a mut FutureSource,
    future: Option<FutureString<'a>>,
}

impl<'a> Stream for StreamAdapter<'a> {
    type Item = <FutureString<'a> as Future>::Output;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> Poll<Option<Self::Item>> {
        if self.future.is_none() {
            self.future.insert(self.future_source.get_next());
        }
        let fut = self.future.as_mut().unwrap();

        match Pin::new(fut).poll(cx) {
            Poll::Ready(value) => {
                self.future = None;
                Poll::Ready(Some(value))
            }
            Poll::Pending => Poll::Pending,
        }
    }
}

编译错误信息

error[E0499]: cannot borrow `self` as mutable more than once at a time
  --> src/main.rs:41:32
   |
41 |             self.future.insert(self.future_source.get_next());
             ----        ------ ^^^^ second mutable borrow occurs here
             |           |
             |           first borrow later used by call
             first mutable borrow occurs here

error: lifetime may not live long enough
  --> src/main.rs:41:32
   |
33 | impl<'a> Stream for StreamAdapter<'a> {
      -- lifetime `'a` defined here
...
37 |         mut self: Pin<&mut Self>,
                       - let's call the lifetime of this reference `'1`
...
41 |             self.future.insert(self.future_source.get_next());
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`

error[E0499]: cannot borrow `self` as mutable more than once at a time
  --> src/main.rs:43:19
   |
33 | impl<'a> Stream for StreamAdapter<'a> {
      -- lifetime `'a` defined here
...
41 |             self.future.insert(self.future_source.get_next());
                                -----------------------------
                                |
                                first mutable borrow occurs here
                                argument requires that `self` is borrowed for `'a`
42 |         }
43 |         let fut = self.future.as_mut().unwrap();
                   ^^^^ second mutable borrow occurs here

error[E0499]: cannot borrow `self` as mutable more than once at a time
  --> src/main.rs:47:17
   |
33 | impl<'a> Stream for StreamAdapter<'a> {
      -- lifetime `'a` defined here
...
41 |             self.future.insert(self.future_source.get_next());
                                -----------------------------
                                |
                                first mutable borrow occurs here
                                argument requires that `self` is borrowed for `'a`
...
47 |                 self.future = None;
                 ^^^^ second mutable borrow occurs here

错误原因分析

  • 可变借用冲突:self.future_source.get_next()返回的FutureString持有&'a mut FutureSource,该借用的生命周期与StreamAdapter的'a绑定,相当于独占了future_source。此时修改self.future(比如insert或设为None)会尝试再次可变借用self,触发Rust的可变借用独占规则。
  • 生命周期不匹配:Stream的poll_next方法接收的self引用生命周期'1无法被编译器证明足够覆盖'a,因为FutureString需要持有'a生命周期的引用,而poll_next的调用周期是不确定的。

安全Rust解决方案

不需要使用unsafe代码,核心是调整FutureSource的状态管理方式,避免独占式可变借用:

方案:使用内部可变性分离引用权限

将FutureSource的内部状态用RefCell(单线程场景)或Mutex/RwLock(多线程异步场景)包裹,让get_next返回的Future持有共享引用而非独占可变引用,从而消除借用冲突。

修改后的代码示例:

use futures_util::Stream;
use std::{cell::RefCell, future::Future, pin::Pin, task::Poll};

// 定义FutureSource的内部状态(示例)
#[derive(Default)]
struct SourceState {
    count: u32,
}

struct FutureSource {
    state: RefCell<SourceState>, // 使用RefCell实现单线程内部可变性
}

impl FutureSource {
    pub fn get_next(&self) -> FutureString {
        FutureString {
            source: self,
            important_state: "example".to_string(),
        }
    }
}

struct FutureString<'a> {
    source: &'a FutureSource, // 改为共享引用
    important_state: String,
}

impl<'a> Future for FutureString<'a> {
    type Output = String;

    fn poll(self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
        // 通过RefCell获取可变权限修改内部状态
        let mut state = self.source.state.borrow_mut();
        state.count += 1;
        Poll::Ready(format!("Item {}", state.count))
    }
}

struct StreamAdapter<'a> {
    future_source: &'a FutureSource,
    future: Option<FutureString<'a>>,
}

impl<'a> Stream for StreamAdapter<'a> {
    type Item = String;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> Poll<Option<Self::Item>> {
        if self.future.is_none() {
            self.future.insert(self.future_source.get_next());
        }
        let fut = self.future.as_mut().unwrap();

        match Pin::new(fut).poll(cx) {
            Poll::Ready(value) => {
                self.future = None;
                Poll::Ready(Some(value))
            }
            Poll::Pending => Poll::Pending,
        }
    }
}

方案说明

  • 内部可变性:RefCell允许在共享引用下修改内部状态,Rust会在运行时检查借用规则,确保同一时间只有一个可变借用。
  • 消除独占借用:FutureString持有&FutureSource而非&mut FutureSource,不会独占整个源,因此StreamAdapter可以自由修改future字段,避免了借用冲突。
  • 多线程适配:如果需要在多线程异步环境中使用,只需将RefCell替换为tokio::sync::Mutex或async_std::sync::Mutex等异步安全的锁结构。

结论

你的需求是健全的,完全可以在安全Rust中实现该适配器,不需要依赖unsafe代码。核心是通过内部可变性调整引用权限,规避Rust的借用检查限制。

内容的提问来源于stack exchange,提问作者Emil Sahlén

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 23:45:08