I have two variables of type &T, x and y, which I swap locally inside a function:
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) {}
This code fails to compile, giving the following error:
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 | | }
| |_____^
I fail to see why it shouldn't work. x and y have different lifetimes, but why should the compiler require y to live as long as x? I am only modifying references locally inside foo and referenced objects are guaranteed to exist. Once foo returns, it doesn't matter if these x and y even existed, does it?
For larger context, I am implementing mergesort and want to swap the primary and auxiliary (temporary) arrays this way.
Obviously,
xandyhave different lifetimes, but why should compiler requireyto live as long asx?
Because of the signature of std::mem::swap:
pub fn swap<T>(x: &mut T, y: &mut T)
T is the type of the argument to foo, which is a reference of some lifetime chosen by the caller of foo. In the 2018 edition of Rust, the latest compiler gives a slightly more detailed error message in which it calls this lifetime '1. Calling std::mem::swap requires the type of x, &'1 T, to be the same as the type of y, but it can't shrink the lifetime of x to match that of y because the lifetime of x is chosen by the caller, not by foo itself. Vikram's answer goes into more detail on why the lifetime cannot be shrunk in this case.
I am essentially only modifying references locally inside foo and referenced objects are guaranteed to exist
This is true, but it doesn't give you any freedom with respect to the lifetime of x inside foo. To make foo compile, you have to give the compiler another degree of freedom by making a new borrow of which the compiler can choose the lifetime. This version will compile (playground):
pub fn foo<T: Copy>(x: &T) {
let mut x = &*x;
...
}
This is called reborrowing, and it happens implicitly in some cases, for example, to the receiver of a method call that takes &mut self. It does not happen implicitly in the case you presented because swap is not a method.
It is helpful to compile this program with the latest stable toolchain on the 2018 Edition, since it improves the error message a bit:
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
What happens is:
x is a reference with the arbitrary lifetime '1 established by the caller.y is a reference created locally, and as such it has lifetime "shorter" than '1.As such, you cannot pass the reference in y to x, even if it may seem safe to, because x expects something that lives at least for the lifetime indicated by the caller.
One possible solution is to create a second copy of the value behind x, and borrow that locally.
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With