rust 为什么异步函数和非异步函数之间的impl trait函数参数的生命周期省略存在差异?

z18hc3ub  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(110)

此代码编译:

async fn foo(_: impl Iterator<Item = &u32>) {}

但是,删除async,它不再工作:

fn foo(_: impl Iterator<Item = &u32>) {}

相反,它导致:

error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:32
  |
1 | fn foo(_: impl Iterator<Item = &u32>) {}
  |                                ^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
1 | fn foo<'a>(_: impl Iterator<Item = &'a u32>) {}
  |       ^^^^                         ^^^

Playground
为什么async和非async函数在生命周期省略方面存在差异?这对我来说毫无意义。看来非async的情况下也可以很容易地遗漏寿命。它只是引入了另一个匿名输入生命周期。

vngu2lb8

vngu2lb81#

在Rust中,函数参数中引用的生命周期有时会被省略,这意味着它们可以被忽略,编译器会自动推断它们。然而,这种自动推理有一些局限性,它并不适用于所有可能的场景。
对于非async函数,生存期省略遵循三条规则:
1.输入位置中的每个省略的寿命变成不同的寿命参数。
1.如果只有一个输入生存期,则将该生存期分配给所有省略的输出生存期。
1.如果有多个输入生存期,但其中一个是&self&mut self,则self的生存期被分配给所有省略的输出生存期。
fn foo(_: impl Iterator<Item = &u32>) {}的情况下,&u32中有一个省略的生存期,它不直接在函数的输入中(它在trait的关联类型中)。这与上面的任何规则都不匹配,因此编译器无法推断出省略的生存期,因此出现了错误。
说到async fn foo(_: impl Iterator<Item = &u32>) {},它编译的原因与async fn的脱糖过程有关。当您声明async fn时,Rust编译器将其转换为返回Future的非异步函数。你的函数的实际签名是这样的:

fn foo<'a>(_: impl Iterator<Item = &'a u32>) -> impl Future<Output = ()> + 'a {}

在这种去糖形式中,&u32的生存期与输出future的生存期相关联,这允许编译器根据上述规则推断出省略的生存期。
在编写async fn时,这种转换对您来说是透明的,但它确实会影响函数处理生存期的方式。这可能有点违反直觉,但这是Rust的生存期省略规则的设计方式以及async fn的实现方式的结果。
因此,您看到的差异是由于async fn经历的省略规则和转换的组合,而不是因为async和非async函数之间处理生命周期的固有差异。

相关问题