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(())
})
}