Temporaries and Lifetime Extension
A “value-to-place coercion” occurs when a value expression is used in a context where a place is needed, e.g. because it is borrowed, matched on, or has a field accessed. Whenever that happens, the value will get stored in a temporary variable. In this step, we make these temporaries explicit.
The rules that determine the scope of these temporaries are complex; they’re described in
[destructors
In this step, for each expression $expr to be coerced, we first add a let tmp; statement,
then assign it tmp = $expr; (these two steps can sometimes be merged), then use tmp where the expression was.
The placement of the let tmp; determines how long the value will live and its drop order.
To get the right scope, extra blocks { .. } may be added.
For example:
#![allow(unused)]
fn main() {
let s = if Option::is_some(&Option::clone(&opt)) {
let _x = &42;
&String::new()
} else {
&String::new()
};
// becomes:
let tmp3;
let tmp4;
let s = if { let tmp1 = Option::clone(&opt); Option::is_some(&tmp1) } {
let tmp2 = 42;
let _x = &tmp2;
tmp3 = String::new();
&tmp3
} else {
tmp4 = String::new();
&tmp4
};
}
Or:
#![allow(unused)]
fn main() {
let opt: RwLock<Option<u32>> = ...
if let Some(x) = Option::as_ref(&*Result::unwrap(RwLock::read(&opt))) {
...
} else {
...
}
// becomes (in edition 2024):
if let tmp = Result::unwrap(RwLock::read(&opt)) && let Some(x) = Option::as_ref(&*tmp) {
...
} else {
...
}
}
Note how in let chains we may introduce the temporaries as part of the let chain to get the
right scope. Our Extended Let Chains allow forward declarations
let x; in the middle of a let chain for that purpose.
Taking an example from the edition book:
#![allow(unused)]
fn main() {
fn f() -> usize {
let c = RefCell::new("..");
c.borrow().len()
}
// Becomes, after method resolution:
fn f() -> usize {
let c = RefCell::new("..");
str::len(*<Ref<_> as Deref>::deref(&RefCell::borrow(&c)))
}
// Before 2024, this becomes:
fn f() -> usize {
let tmp1; // Added at the start of scope so that it drops after the other locals.
let tmp2;
let c = RefCell::new("..");
tmp1 = RefCell::borrow(&c); // error[E0597]: `c` does not live long enough
tmp2 = <Ref<_> as Deref>::deref(&tmp1);
str::len(*tmp2)
}
// After 2024, this becomes:
fn f() -> usize {
let c = RefCell::new("..");
let tmp1; // drops before other locals
let tmp2;
tmp1 = RefCell::borrow(&c);
tmp2 = <Ref<_> as Deref>::deref(&tmp1);
str::len(*tmp2)
}
}
There is an exception to the above: temporaries can, when sensible
[destructors
#![allow(unused)]
fn main() {
let x = &1 + 2;
// becomes:
static TMP: u32 = 1 + 2;
let x = &TMP; // this allows `x` to have type `&'static u32`
}
After this step, all place contexts contain place expressions.
The rest of this section is a work-in-progress experiment about making the book executable.
pub fn desugar_value_to_place(program: &mut Program) -> Result<(), CompilationError> {
program.visit_all_mut(|_block: &mut BlockExpression| {
// TODO: look for all the `VirtualExpression::ValueToPlaceCoercion`
Ok(())
})
}