下面是一个简单的示例,其中您只需要指定可变引用的生存期:

fn foo<'a>(x: &'a [&'a i32]) -> impl Iterator<Item = ()> + 'a {
    x.iter().map(|_| ())
}

fn foo_mut<'a>(x: &'a mut [&'a i32]) -> impl Iterator<Item = ()> + 'a {
    x.iter().map(|_| ())
}

fn bar(x: &[&i32]) {
    for _ in foo(x) {}
}

// why is an explicit lifetime needed?
fn bar_mut<'a>(x: &'a mut [&'a i32]) {
    for _ in foo_mut(x) {}
}

我希望不必指定这些生存期,因为这会使调用函数变得更加困难.

这只是某个编译器错误,还是我遗漏了什么?

您可以通过使用宏来解决这个问题,但我真的希望能够将其定义为一个函数,而不指定生存期.

推荐答案

首先,让我们谈谈lifetime elision个.所有引用都有一段与之相关的生存期,但Rust允许您在可以适当推断的情况下省略注释.

如果你要写:

fn bar_mut(x: &mut [&i32]) {
    for _ in foo_mut(x) {}
}

编译器会推断每个引用都有自己的生命周期:

fn bar_mut<'a, 'b>(x: &'a mut [&'b i32]) {
    for _ in foo_mut(x) {}
}

正如您所看到的,bar_mut的签名不同于foo_mut的签名;片&[_]的生命周期不必与其元素&i32的生命周期匹配,因此它不能满足foo_mut设置的约束.


那么,为什么它对foobar都有效呢?

第二,让我们谈谈variance个.特别是,生命周期可以由编译器调整(根据它们的变化),通常调整到需要它们的最小帧.通常,引用的生存期称为covariant,这意味着编译器可以根据需要缩短它们.

如果我们看一下有明确生命周期 的bar个:

fn bar<'a, 'b>(x: &'a [&'b i32]) {
    for _ in foo(x) {}
}

当超过xfoo时,'b的生命周期 缩短到与'a匹配,它可以做到这一点,因为'b是协变的.由于嵌套引用的构造,还有一个隐含的'b: 'a约束,这保证了它是有效的,但目前这并不重要.

然而,mutable个引用稍微严格一些,因为它们的内容可以重新分配.可变引用的生存期仍然是协变的,但其类型references必须是invariant.否则,您将能够通过该引用分配一个生命周期短于其实际需要的值.所以,如果我们看一下你foo_mut英镑的签名:

fn foo_mut<'a>(x: &'a mut [&'a i32]) -> ...

与片段&mut [_]相关联的生命周期 可以是协变的,但是元素&i32must的生命周期 是不变的,因为它在可变引用之后.因此,'a是不变的.因此,当我们观察bar_mut个不同生命周期 的人时:

fn bar_mut<'a, 'b>(x: &'a mut [&'b i32]) {
    for _ in foo_mut(x) {}
}

生命周期 'b同样是不变的,因此当它通过到foo_mut时保持不变.由于foo_mut要求生命周期 相同,因此'a必须与'b匹配.而'b不能被缩短,所以'a必须是extended,但这是不允许的,因为'a只是covariant(即它只能被缩短).以下是获取错误的方法:

error: lifetime may not live long enough
  --> src/lib.rs:15:14
   |
14 | fn bar_mut<'a, 'b>(x: &'a mut [&'b i32]) {
   |            --  -- lifetime `'b` defined here
   |            |
   |            lifetime `'a` defined here
15 |     for _ in foo_mut(x) {}
   |              ^^^^^^^^^^ argument requires that `'a` must outlive `'b`

我真的希望能够在不指定生存期的情况下将其定义为函数.

你之所以陷入困境,只是因为foofoo_mut不必要地被过度约束了.如果您只将生命周期注释为需要的内容,则根本就没有这个问题:

fn foo<'a>(x: &'a [&i32]) -> impl Iterator<Item = ()> + 'a {
    x.iter().map(|_| ())
}

fn foo_mut<'a>(x: &'a mut [&i32]) -> impl Iterator<Item = ()> + 'a {
    x.iter().map(|_| ())
}

fn bar(x: &[&i32]) {
    for _ in foo(x) {}
}

fn bar_mut(x: &mut [&i32]) {
    for _ in foo_mut(x) {}
}

如图所示,您只需要外部生命周期,因为这是您的迭代器类型所依赖的.从技术上讲,它也依赖于&i32的生存期,但因为嵌套引用的生存期受到内在的限制,所以您可以免费获得它.您仍然需要'a,因为您需要告诉编译器允许您的impl Iterator引用什么.

关键是,您应该尽可能使用不同的生存期,并且在不必要的时候省略显式.playground上的完整代码.


最后要注意的是:因为&'a mut [&'a i32]是过度约束,所以您稍后会遇到问题usingbar_mut.例如,您不能对相同的值调用两次.看到playground号公路上的那个故障了.参见Why does this mutable borrow live beyond its scope?,它在不同的场景中解释了更多.

Rust相关问答推荐

为什么是!为Rust中的RwLockReadGuard和RwLockWriteGuard实现的发送特征?

将JSON密钥转换为Polars DataFrame

铁 rust 干线无法使用PowerShell获取环境变量

将大小为零的类型实例存储到空指针中

有没有更好的方法从HashMap的条目初始化 struct ?

在不重写/专门化整个函数的情况下添加单个匹配手臂到特征的方法?

Rust类似功能是C++命名空间吗?

可以为rust构建脚本编写单元测试吗?

在Rust中判断编译时是否无法访问

将Vec<;U8&>转换为Vec<;{Float}&>

为什么`tokio::main`可以直接使用而不需要任何导入?

Boxing 如何将数据从堆栈移动到堆?

当推送到 HashMap 中的 Vector 时,类型 `()` 无法取消引用

为什么这段 Rust 代码会在没有递归或循环的情况下导致堆栈溢出?

如何连接 Rust 中的相邻切片

发生移动是因为 `data` 的类型为 `Vec`,它没有实现 `Copy` 特性

如何在 C++ 和 Rust 之间共享 pthread 同步原语?

如何构建包含本地依赖项的 docker 镜像?

当值是新类型包装器时,对键的奇怪 HashMap 生命周期要求

在同一向量 Rust 中用另一个字符串扩展一个字符串