函数指针的生命周期是 for<'a, '_> 而它应该是 for<'r>

lifetime of function pointer is for<'a, '_> while it should be for<'r>

有时我会在一生中挣扎。我还在学习,我不知道这里发生了什么:

use std::future::Future;
use futures::future::{BoxFuture, FutureExt};

struct M{}
struct Client{}

impl Client {
    async fn send_and_expect<'a>(
        &'a mut self,
        m: &M
    ) -> std::result::Result<(), ()> {
        Ok(())
    }
    
    pub fn connection_retrier<'a, T>(
        f: fn(&'a mut Self, &M) -> T,
        f_self: &'a mut Self,
        f_m: &'a M,
    )-> BoxFuture<'a, std::result::Result<(),()>>
    where
        T: Future<Output = std::result::Result<(), ()>> + 'a
    {
        async move {
            Client::send_and_expect(f_self, f_m).await
        }.boxed()
    }
    
    async fn send_with_retry<'a>(&'a mut self) -> std::result::Result<(), ()> {
        let m = M{};
        Client::connection_retrier(
                    Client::send_and_expect, 
                    &mut *self, &m).await
    }
}

错误:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/lib.rs:31:21
   |
11 |     ) -> std::result::Result<(), ()> {
   |          --------------------------- the `Output` of this `async fn`'s found opaque type
...
31 |                     Client::send_and_expect, 
   |                     ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `for<'r> fn(&mut Client, &'r M) -> impl futures::Future`
              found fn pointer `for<'a, '_> fn(&'a mut Client, &M) -> impl futures::Future

Playground

我现在完全不明白为什么在 for<'r> fn(&mut Client, &'r M) -> impl futures::Future 中,&mut Client 没有生命周期。 _ for<'a, '_> fn(&'a mut Client, &M) -> impl futures::Future` 中意味着什么?

我非常想了解这里发生的事情。

清理代码

为了从编译器的角度看这段代码,让我们把这个例子中的所有生命周期参数都改成显式的并且有不同的名字,展开async fn糖,然后看看错误是如何变化的.

上面的例子在生命周期推断和脱糖后等价于下面的例子

// name the second lifetime, and expand the async fn sugar.
fn send_and_expect<'a, 'b>(&'a mut self, m: &'b M) -> impl Future<Item=Result<(), ()>> + 'a + 'b 
{ ... }

// rename 'a to 'c to avoid ambiguous lifetime names
pub fn connection_retrier<'c, T>(
        f: for<'d> fn(&'c mut Self, &'d M) -> T, // name the implicit higher-ranked lifetime here
        f_self: &'c mut Self,
        f_m: &'c M,
    )-> BoxFuture<'c, std::result::Result<(), ()>>
where T: Future<Output = std::result::Result<(), ()>> + 'c 
{ ... }

// rename 'a to 'e to avoid ambiguous lifetime names
async fn send_with_retry<'e>(&'e mut self) -> std::result::Result<(), ()> {

进行此更改后,错误变为:

Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/lib.rs:31:21
   |
11 |     ) -> std::result::Result<(), ()> {
   |          --------------------------- the `Output` of this `async fn`'s found opaque type
...
31 |                     Client::send_and_expect, 
   |                     ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `for<'d> fn(&mut Client, &'d M) -> impl futures::Future`
              found fn pointer `for<'a, 'b> fn(&'a mut Client, &'b M) -> impl futures::Future`

这应该可以澄清您关于 '_ 的问题:它只是编译器为推断的第二生命周期参数 send_and_expect 提供的名称。至于 &mut Client 上缺失的生命周期,你可以看到这里仍然缺失。由于我不完全理解的原因,并且以依赖于给出的确切错误消息的方式,编译器有时会在打印引用类型时省略具体的生命周期,但不要搞错,该引用的生命周期 is 'c.

解决错误

进入实际问题。 f 的签名表明 connection_retrier 需要一个函数,该函数 (1) 引用生命周期 &'c 和 (2) any[=79= 的引用] 其他生命周期,以及 (3) returns 一个 Future 类型,只要 'c 有效,它就会保持有效,正如您的 where 绑定所指定的那样。

当我们将 send_and_expect 传递给 connection_retrier 时,为了使签名匹配,编译器将其强制转换为 for<'d> send_and_expect::<'c, 'd> 类型。但是那个类型不符合上面的条件(3)!与常规函数不同,async 函数的默认行为是在其 return 类型中捕获 all 输入生命周期,因此 return 类型for<'d> send_and_expect::<'c, 'd> 实际上是 impl Future<Item=Result<(), ()>> + 'c + 'd,您可以通过查看 send_and_expect 的扩展签名来判断。

由于此类型借用了 'c'd 这两个生命周期,并且没有限制 'd: 'c(阅读:'d'c ),如果 'd 先结束,它可能不会在整个生命周期 'c 内保持有效。正是这种不匹配导致您收到相当神秘的生​​命周期错误。有两种方法可以解决此问题,具体取决于您喜欢的语义。您可以:

  • f 中完全删除排名较高的边界,并在 connection_retrier 中准确指定您将调用它的生命周期(两者都是 &'c。)

    pub fn connection_retrier<'c, T>(
            f: fn(&'c mut Self, &'c M) -> T
            f_self: &'c mut Self,
            f_m: &'c M,
        ) -> BoxFuture<'c, std::result::Result<(), ()>>
    where T: Future<Output = std::result::Result<(), ()>> + 'c 
    { ... }
    
  • 保持 connection_retrier 的签名不变,并指定由 send_and_expect 编辑的未来 return 仅借用其第一个参数。为此,您需要在签名上添加 async fn 糖并将正文包裹在 async move 块中。

    fn send_and_expect<'a, 'b>(&'a mut self, m: &'b M) -> impl Future<Output=Result<(), ()>> + 'a 
    { async move { ... } }
    

请注意,您 不能 通过将 f 的类型写为 for<'d: 'c> fn(&'c mut Self, &'d M) -> T 来解决此问题,因为目前普遍量化的生命周期不允许使用边界。