Swapping two local references leads to lifetime error

  • A+
Category:Languages

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, x and y have different lifetimes, but why should compiler require y to live as long as x?

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.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: