交换两个本地引用会导致生命周期错误

kre*_*reo 7 lifetime rust borrow-checker borrowing

我有两个类型的变量&T,x并且y我在函数内部交换:

pub fn foo<T: Copy>(mut x: &T) {
    let y_owned = *x;
    let mut y = &y_owned;
    for _ in 0..10 {
        do_work(x, y);
        std::mem::swap(&mut x, &mut y);
    }
}

fn do_work<T>(_x: &T, _y: &T) {}
Run Code Online (Sandbox Code Playgroud)

此代码无法编译,给出以下错误:

error[E0597]: `y_owned` does not live long enough
 --> src/lib.rs:3:22
  |
3 |         let mut y = &y_owned;
  |                      ^^^^^^^ borrowed value does not live long enough
...
8 |     }
  |     - borrowed value only lives until here
  |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 1:5...
 --> src/lib.rs:1:5
  |
1 | /     pub fn foo<T: Copy>(mut x: &T) {
2 | |         let y_owned = *x;
3 | |         let mut y = &y_owned;
4 | |         for _ in 0..10 {
... |
7 | |         }
8 | |     }
  | |_____^
Run Code Online (Sandbox Code Playgroud)

我不明白为什么它不应该工作.x并且y有不同的生命周期,但为什么编译器需要y生存时间长x?我只修改本地内部的foo引用,并保证引用的对象存在.一旦foo返回时,它并不重要,如果这些xy甚至存在了,不是吗?

对于更大的上下文,我正在实现mergesort并希望以这种方式交换主要和辅助(临时)数组.

tre*_*tcl 8

显然,x并且y有不同的生命周期,但为什么编译器需要y生存只要x

由于签名std::mem::swap:

pub fn swap<T>(x: &mut T, y: &mut T)
Run Code Online (Sandbox Code Playgroud)

T是参数的类型foo,它是调用者选择foo的某些生命周期的参考.在2018版的Rust中,最新的编译器提供了一个稍微更详细的错误消息,在其中调用了这个生命周期'1.调用std::mem::swap要求的类型为x,&'1 T与要的类型相同y,但它不能缩短与生命周期相x匹配y的生命周期,因为生命周期x是由调用者选择的,而不是由foo它自己选择的.维克拉姆的答案详细阐述了为什么在这种情况下生命不能缩小的原因.

我基本上只修改foo内部的引用,并保证引用的对象存在

这是事实,但它并没有给你任何关于x内部生命的自由foo.要进行foo编译,必须通过新的借用为编译器提供另一个自由度,编译器可以选择生命周期.这个版本将编译(playground):

pub fn foo<T: Copy>(x: &T) {
    let mut x = &*x;
    ...
}
Run Code Online (Sandbox Code Playgroud)

这称为重新借用,它在某些情况下隐式发生,例如,发生在接受方法调用的接收器上&mut self.在您呈现的情况下,它不会隐式发生,因为swap它不是一种方法.


E_n*_*ate 5

使用2018 Edition上最新的稳定工具链编译此程序很有帮助,因为它改进了错误消息:

error[E0597]: `y_owned` does not live long enough
 --> src/lib.rs:4:17
  |
1 | pub fn foo<T: Copy>(mut x: &T) {
  |                            - let's call the lifetime of this reference `'1`
...
4 |     let mut y = &y_owned;
  |                 ^^^^^^^^
  |                 |
  |                 borrowed value does not live long enough
  |                 assignment requires that `y_owned` is borrowed for `'1`
...
9 | }
  | - `y_owned` dropped here while still borrowed
Run Code Online (Sandbox Code Playgroud)

会发生什么:

  • 输入x'1由调用者建立的任意生命周期的引用.
  • 该变量y是在本地创建的引用,因此它的生命周期"短" '1.

因此,您无法将引用传递yx,即使它似乎是安全的,因为x期望某些内容至少在调用者指示的生命周期内存在.

一种可能的解决方案是在x后面创建值的第二个副本,并在本地借用.

pub fn foo<T: Copy>(x: &T) {
    let mut x = &*x;
    let mut y = &*x;
    for _ in 0..10 {
        do_work(x, y);
        std::mem::swap(&mut x, &mut y);
    }
}
Run Code Online (Sandbox Code Playgroud)