为什么 GAT、生命周期和异步的这种组合需要 `T: 'static`?

问题描述 投票:0回答:1

这段奇怪的代码会从

do_the_thing()
发出错误,表示
T
的寿命不够长:

use std::future::Future;

trait Connection: Send {
    type ExecFut<'a>: Future<Output = ()> + Send
    where
        Self: 'a;

    fn transaction<F>(&mut self, _f: F)
    where
        F: for<'a> FnOnce(&'a mut Self) -> Box<dyn Future<Output = ()> + Send + 'a> + Send,
    {
        unimplemented!()
    }

    fn execute<'a>(&'a mut self) -> Self::ExecFut<'a> {
        unimplemented!()
    }
}

fn do_the_thing<T: Connection>(connection: &mut T) {
    connection.transaction(|conn| {
        Box::new(async move {
            conn.execute().await;
        })
    });
}
error[E0310]: the parameter type `T` may not live long enough
  --> src/main.rs:22:9
   |
22 | /         Box::new(async move {
23 | |             conn.execute().await;
24 | |         })
   | |__________^ ...so that the type `T` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound...
   |
20 | fn do_the_thing<T: Connection + 'static>(connection: &mut T) {
   |                               +++++++++

游乐场看到它。

据我所知,不应该存在需要

'static
的隐含界限。几乎所有事物都受到
'a
的限制,并且
T
中的任何生命周期都应该总是比它长。虽然显然我可能缺少一些东西。

我还发现了一些没有帮助的“解决方案”:

  1. 按照编译器的建议添加
    'static
    是不合理的。它可以解决,但我想使用具有非
    Connection
    生命周期的真正
    'static
  2. 出于某种原因,删除
    Send
    上的
    dyn Future
    绑定可以使其通过编译。这对我来说毫无意义。
  3. 如果我将函数设为非泛型,但使用不是
    Connection
    'static
    ,它也会编译,这似乎与编译器错误相矛盾。

以上并非真实代码;最初的动机源于使用 diesel-async

 板条箱中的 
AsyncConnection 和类似的代码结构。但是,我希望它能够代表核心问题,并且通过了解这里的问题和潜在的解决方案,可以对其进行调整。

rust async-await lifetime generic-associated-types
1个回答
0
投票

通过进一步的修补和研究,我发现了这个问题 - GAT:决定是否对

where Self: 'a
有默认值 - 关于
where
上的
type ExecFut<'a>
子句。从该问题中的评论来看,其他人似乎因执行此限制而遇到了类似的错误。

该问题提供了一种解决方法,即将您的特征分为两部分 - 一个用于关联类型,另一个用于方法,以便可以省略

where Self: 'a
子句。在我的示例中这样做可以使其编译:

trait Connection: Send {
    type ExecFut<'a>: Future<Output = ()> + Send;
}

trait ConnectionExt: Connection {
    fn transaction<F>(&mut self, _f: F)
    where
        F: for<'a> FnOnce(&'a mut Self) -> Box<dyn Future<Output = ()> + Send + 'a> + Send,
    {
        unimplemented!()
    }

    fn execute<'a>(&'a mut self) -> Self::ExecFut<'a> {
        unimplemented!()
    }
}

impl Connection for &'_ i32 {
    type ExecFut<'a> = std::future::Ready<()>;
}

impl<C> ConnectionExt for C where C: Connection {}

fn do_the_thing<T: Connection>(connection: &mut T) {
    connection.transaction(|conn| {
        Box::new(async move {
            conn.execute().await;
        })
    });
}

我仍然需要看看这在柴油机异步情况下是否是一个可用的解决方案。

作为旁注,还有另一个相关问题 - GAT、异步和发送边界问题 - 描述了上述边界和

Send
之间的奇怪交互,这 可能 解释了为什么要删除
Send
bound 导致代码片段编译。还是很奇怪。

© www.soinside.com 2019 - 2024. All rights reserved.