为什么不编译具有两个非静态引用和一个静态引用的异步函数?

Why doesn't an async function with two non-static references and a static reference compile?

具有一个静态引用的异步函数编译:

pub async fn test_0(_a: &'static str)  {
}

具有非静态和静态引用的异步函数编译:

pub async fn test_1<'a>(_a: &'a str, _b: &'static str)  {
}

具有三个非静态引用的异步函数编译:

pub async fn test_2<'a, 'b, 'c>(_a: &'a str, _b: &'b str, _c: &'c str)  {
}

接受两个非静态引用和一个静态引用的函数和 returns 未来编译:

pub fn test_3_desugared<'a, 'b>(_a: &'a str, _b: &'b str, _c: &'static str) -> impl std::future::Future<Output=()> {
  std::future::ready(())
}

那么为什么采用两个非静态引用和一个静态引用的异步函数不能编译?

pub async fn test_3<'a, 'b>(_a: &'a str, _b: &'b str, _c: &'static str)  {
}

问题中的编译错误:

error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
  --> src/lib.rs:11:74
   |
11 | pub async fn test_3<'a, 'b>(_a: &'a str, _b: &'b str, _c: &'static str)  {
   |                                                                          ^
   |
note: hidden type `impl Future` captures lifetime smaller than the function body
  --> src/lib.rs:11:74
   |
11 | pub async fn test_3<'a, 'b>(_a: &'a str, _b: &'b str, _c: &'static str)  {
   |      

游乐场link供参考:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4d90427b4a592ccc3b3d119a326cc401

我当前的 Rust 版本:

$ rustc --version
rustc 1.56.1 (59eed8a2a 2021-11-01)

我在尝试向已有两个引用参数的函数中添加环摘要算法参数时遇到了这个问题。解决方法非常简单,因为我可以将静态引用包装在一个结构中,将其传入,然后毫无问题地使用它。我只是好奇为什么会这样。

“impl Future”版本编译
pub fn test_3<'a, 'b>(_a: &'a str, _b: &'b str, _c: &'static str) -> impl Future
{
   async {}
}
异步版本无法编译
pub async fn test_3<'a, 'b>(_a: &'a str, _b: &'b str, _c: &'static str)  {
}

impl Future版本之所以编译是因为_a_b_c引用没有被future捕获。编译器足够聪明,可以删除引用,我们可以通过函数的 HIR 看到这一点。

pub fn test_3<'a, 'b>(_a: &'a str, _b: &'b str, _c: &'static str)
 -> /*impl Trait*/ { #[lang = "from_generator"](|mut _task_context| { }) }

您可以将其与异步版本的HIR进行比较。在这种情况下,引用都被捕获了。

pub async fn test_3<'a, 'b>(a: &'a str, b: &'b str, c: &'static str)
 -> /*impl Trait*/ where
 'a:'b #[lang = "from_generator"](move |mut _task_context|
                                      {
                                          let a = a;
                                          let b = b;
                                          let c = c;
                                          { let _t = { () }; _t }
                                      })

在异步版本中,函数的所有输入生命周期都在异步函数返回的未来捕获,就像这样 impl Future + 'a + 'b + 'static。最后,所有这些生命周期被编译器统一为一个生命周期'0。这种统一是由成员约束算法完成的,通过“最少选择”进入一组生命周期。

对于我们的具体情况,我们得到了 '0 min(['a, 'b, 'static])。问题是这个集合中没有明显的最小值,因为编译器不知道 'a 和 'b 之间的关系。

如果您将此信息提供给编译器,异步版本会编译

pub async fn test_3<'a, 'b>(a: &'a str, b: &'b str, c: &'static str) 
    where 'a:'b
{
   ()
}

另一种解决方案是在没有明显的最少选择的情况下选择 'static。在此PR#89056

中讨论