rust - 使用产生 Stream 的异步 fn 的通用结构遇到问题
问题描述
从标题可以看出,这个例子中有很多概念发生冲突:
我正在尝试做的事情:
- 我有一个通用的异步函数,它产生一些结果
T
并采用一个简单的i32
参数(页码) - 结构
Pager
持有这个异步fetch
函数 Pager
的fn 使用并递增页码在函数返回的s 上stream
生成 a 。Stream
T
fetch
stream::unfold
我为使其发挥作用所做的努力令人沮丧。我认为那是因为我只有一个月的时间来学习 Rust,解决这个问题需要同时使用泛型、异步/等待、闭包和其他一些概念。我觉得我很接近,但我承认我很困。当前的错误与生命周期有关,我无法破译它:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
我有两个问题:
- 我会以正确的方式解决这个问题吗?这个例子在结构上有什么可以简化的吗?我正在从我想要使用此代码的方式向后工作(如在
main
操场的 fn 中:创建一个寻呼机,我需要一个来包装一个 fetch 函数)。 - 我怎样才能克服这个错误(以及接下来出现的任何错误)以使这个精简的示例正常工作?
感谢您阅读本文以及您可以提供的任何帮助!
解决方案
编译器试图(有点神秘地)告诉你,它不能确定实例在被调用Pager
时不会被删除。self.fetch
实际上,由于您断言Stream
应该具有'static
生命周期,因此编译器正在尝试确保在此后的self.fetch
任何时间调用它都是安全的——这将包括在Pager
被删除之后(无论何时)。但是一旦删除,self.fetch
(可以找到指向 fetch 函数的指针的位置)将被释放,并且您正在尝试 use-after-free。
那么,解决方案是什么?有两种选择:
断言
Pager
实例的寿命超过Stream
:fn stream<'a>(&'a self) -> Pin<Box<dyn Stream<Item = T> + 'a>> { ... }
如果您采用这种方法,那么您必须考虑从其环境中
unfold
捕获的闭包参数。&'a self
默认情况下,它通过在闭包内部借用&'a self
-leaving you with来做到这一点(外部借用存在于函数调用&&'a self
的其余部分);stream
再一次,这还不够长——你需要一个持续生命周期的借用Stream
,它move
可以通过将 移动&'a self
到闭包中来给你:stream::unfold(0, move |state| ...);
此外,
async
闭包中的块还捕获其环境——再次默认借用,这与闭包存在相同的问题。所以你需要现在move
从&'a self
闭包进入async
块:async move { ... }
(你已经在这样做了)。
给我们整体:
fn stream<'a>(&'a self) -> Pin<Box<dyn Stream<Item = T> + 'a>> { let stream = stream::unfold(0, move |state| async move { let yielded = (self.fetch)(state).await; let next_state = state + 1; Some((yielded, next_state)) }); Box::pin(stream) }
在操场上看到它。
或者,由于
self.fetch
是一个函数指针而不是一个闭包(指针对象不捕获任何状态)并且unfold
不从 捕获任何其他状态Pager
,那么如果调用fetch
时的值在unfold
此后永远保持有效(即使Pager
被删除),您可以简单地将函数指针复制出来,self
以便可以将其移动到闭包中:fn stream(&self) -> Pin<Box<dyn Stream<Item = T>>> { let fetch = self.fetch; let stream = stream::unfold(0, move |state| async move { let yielded = fetch(state).await; let next_state = state + 1; Some((yielded, next_state)) }); Box::pin(stream) }
(和以前一样,我们仍然需要
move
将此复制的函数指针放入闭包中,然后再放入async
块中,因为仅仅借用局部fetch
变量不会持续足够长的时间)。请注意,此方法要求将
F
类型参数限制为'static
生命周期:F: Future<Output = T> + 'static,
在操场上看到它。
推荐阅读
- c# - OmniSharp 对 VS Code 的 C# 扩展突然停止加载我的 csproj 文件?
- azure - 如何使用 Arm 模板创建 azure blob 存储?
- python - 从时间戳数组中获取延迟数组
- php - 在订单详细信息中显示自定义 WooCommerce 结帐字段并使其可编辑
- revit-api - 为什么“ElementId(BuiltInCategory.OST_Walls)”在 Revit API 2019 中失败?
- c# - 获取数据库名称列表
- r - 如何在 R 中创建垃圾箱
- java - 尝试编写 Caesar Cipher 程序 - 我的加密方法有问题。[爪哇]
- networking - 如何重复ansible任务直到结果失败+显示每次重试的时间戳?
- java - Axon - 投影或事件丰富器?