Rust如何延长本地变量生命周期 实现分页GraphQL API转TryStream
核心解决思路
你不需要强行延长本地变量生命周期,只要把查询实例的所有权转移给返回的TryStream即可,解决FnMut闭包无法重复持有所有权的问题可以用两种方案:
方案1:用Arc包裹查询实例(推荐)
因为你的查询本身很轻量,Arc的克隆开销可以忽略不计,把查询包成Arc后传入闭包,每次调用仅需共享访问即可,不会转移所有权。
use std::sync::Arc; fn get_paginated_entities<T>( &self, query: T, ) -> impl TryStream<Ok = T::ResponseData, Error = anyhow::Error> + '_ where T: PaginatedEntityQuery + 'static, { // 将查询放入Arc实现共享所有权 let query = Arc::new(query); try_unfold(StreamState::Start, move |state| { // 每次调用闭包克隆Arc,开销极低 let query = query.clone(); async move { // 处理流状态得到cursor逻辑省略 let data = query.get_data(self, cursor).await?; // 数据处理与下一个状态计算逻辑省略 Ok(Some((data, next_state))) } }) }
调用逻辑直接传所有权的查询实例即可:
fn get_some_entities(filter: String, backend: &Backend) -> impl TryStream<Ok = Vec<SomeOtherType>, Error = anyhow::Error> + '_ { // 直接创建查询实例,所有权会被转移给返回的Stream let query = SomeEntityQuery { filter }; backend.get_paginated_entities(query) .map_ok(|data| /* 你的转换逻辑 */ todo!()) }
方案2:直接克隆查询实例
如果你的查询实现了Clone,且本身轻量到克隆开销可以完全忽略,也可以不用Arc,每次闭包调用时直接克隆一份查询实例:
fn get_paginated_entities<T>( &self, query: T, ) -> impl TryStream<Ok = T::ResponseData, Error = anyhow::Error> + '_ where T: PaginatedEntityQuery + Clone, { try_unfold(StreamState::Start, move |state| { // 每次调用直接克隆查询实例 let query = query.clone(); async move { let data = query.get_data(self, cursor).await?; // 后续逻辑省略 Ok(Some((data, next_state))) } }) }
补充说明
你原来的get_data方法签名没有绑定&self的生命周期,如果你的实现中返回的Future会持有查询实例的引用,需要补充生命周期约束避免悬垂:
trait PaginatedEntityQuery { type ResponseData; fn get_cursor(data: &Self::ResponseData) -> Option<String>; fn has_next_page(data: &Self::ResponseData) -> bool; // 补充&self的生命周期约束,保证查询实例存活时间不短于返回的Future fn get_data<'a, 's>( &'s self, backend: &'a Backend, cursor: Option<String>, ) -> Pin<Box<dyn Future<Output = Result<Self::ResponseData>> + 'a>> where 's: 'a; }
如果你的Future不会持有查询实例的引用(比如调用get_data时就把需要的字段都复制走了),则不需要补充这个约束。
内容的提问来源于stack exchange,提问作者Aleksandar Dimitrov
相关产品推荐
相关产品推荐

