首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >锈蚀异步借用寿命

锈蚀异步借用寿命
EN

Stack Overflow用户
提问于 2021-12-31 01:20:14
回答 2查看 530关注 0票数 4

我正在尝试制作一个允许异步链接副作用的助手,但我无法获得正确的泛型边界,以便编译器能够理解未来的输出,而不是用于构建它的引用。

操场连接

它的要旨在于:

代码语言:javascript
复制
struct Chain<T> {
    data: T
}
impl<T> Chain<T> {
    pub async fn chain<E, Fut, F>(self, effect: F) -> Result<T, E>
        where
            Fut: Future<Output=Result<(), E>>,
            F: FnOnce(&T) -> Fut
    {
        todo!()
    }
}

给出一个编译器错误

代码语言:javascript
复制
error: lifetime may not live long enough
  --> src/main.rs:39:32
   |
39 |     let r = chain.chain(|this| this.good("bar")).await;
   |                          ----- ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |                          |   |
   |                          |   return type of closure `impl Future` contains a lifetime `'2`
   |                          has type `&'1 MyData`

如果我们修复chain,以便它可以推断引用在与未来相同的生存期内可用:

代码语言:javascript
复制
impl<T> Chain<T> {
    pub async fn chain<'a, E, Fut, F>(self, effect: F) -> Result<T, E>
        where
            T: 'a, 
            Fut: 'a + Future<Output=Result<(), E>>,
            F: FnOnce(&'a T) -> Fut
    {
        effect(&self.data).await?;
        Ok(self.data)
    }
}

我们得到一个新的编译器错误移动self.data时,它仍然是借来的。

代码语言:javascript
复制
error[E0505]: cannot move out of `self.data` because it is borrowed
  --> src/main.rs:30:12
   |
23 |     pub async fn chain<'a, E, Fut, F>(self, effect: F) -> Result<T, E>
   |                        -- lifetime `'a` defined here
...
29 |         effect(&self.data).await?;
   |         ------------------
   |         |      |
   |         |      borrow of `self.data` occurs here
   |         argument requires that `self.data` is borrowed for `'a`
30 |         Ok(self.data)
   |            ^^^^^^^^^ move out of `self.data` occurs here

我想有一个沿着|this| futures::future::ready(Err(this))的病理性终结,这将导致早期的回报,借仍然“活着”。

问题

我们怎样才能让chain工作呢?我正常的一生中的分块范围似乎没什么用。是否有一组where约束可以被添加以证明借款和最终移动是在不相交的生命周期上?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-12-31 06:01:54

在这种特殊情况下,当前的约束语法和缺乏高级类型不允许您表达所需的内容。

可以使用高等级性状界 ( for<'a>语法)在where子句中引入中间泛型生存期参数'a,以指示约束必须在任何生存期内有效。这在这里是必要的,并且您的第一个补丁不能工作的原因是,'a作为chain上的一个泛型,意味着生存期由调用方决定,然而,self的生存期是由构造决定的,而不是由调用方选择的任何生命周期。因此,稍微正确的语法(并与未加糖的原始代码相同)将是:

代码语言:javascript
复制
pub async fn chain<E, Fut, F>(self, effect: F) -> Result<T, E>
    where
        Fut: Future<Output = Result<(), E>>,
        F: for<'a> FnOnce(&'a T) -> Fut
{
    ...

但这一点也没有帮助,因为Fut'a之间仍然没有任何关联。不幸的是,没有办法跨多个约束使用相同的for<'a>。您可以尝试一次使用impl Trait来定义它,但不支持:

代码语言:javascript
复制
pub async fn chain<E, F>(self, effect: F) -> Result<T, E>
    where F: for<'a> FnOnce(&'a T) -> (impl Future<Output = Result<(), E>> + 'a)
{
    ...
代码语言:javascript
复制
error[E0562]: `impl Trait` not allowed outside of function and method return types
  --> src/lib.rs:35:44
   |
35 |         where F: for<'a> FnOnce(&'a T) -> (impl Future<Output = Result<(), E>> + 'a)
   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

有希望有更好的支持更高类型在未来。通过使用几乎完全的通用关联类型特性,这个特定的案例可能有一个夜间解决方案,但我还没有找到它。

因此,唯一真正的修复方法是使用命名类型作为返回值,这实际上只留给我们特性对象:

代码语言:javascript
复制
use std::pin::Pin;
use futures::future::FutureExt;

pub async fn chain<E, F>(self, effect: F) -> Result<T, E>
    where F: for<'a> FnOnce(&'a T) -> Pin<Box<dyn Future<Output = Result<(), E>> + 'a>>
{
    ...

let r = chain.chain(|this| this.good("bar").boxed()).await;

顺便提一句,您的bad大小写仍然不编译,而且实际上无法工作,因为您将返回对本地值的引用。

票数 4
EN

Stack Overflow用户

发布于 2021-12-31 03:11:51

看起来您正在尝试实现future.then()

如果您知道这一点,并且正在将其作为练习进行,那么您可能应该设计它,使效果方法返回值,并使用这些值从链式方法返回。这样你就可以执行适当的操作顺序。就我所理解的您的设计而言,在chain方法中等待生效并不会使您受益,因为您的skip函数也是异步的,并且将返回将来( chain方法的实际返回类型是Future>,因为异步是这样工作的:它在将来包装了您的显式返回类型)。

所以没有必要等待链子内的效果,只要你使用它,你仍然要等待它--在你真正等待它之前,不会发生任何事情

TL;DR i会将您的效果方法转换为返回值,并安排链只返回这些值。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70538177

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档