#lifetime #value #temporary #reference

no-std temporaries

Make temporary values useable in a fairly normal style through macros. This nicely wraps the hacks that work around temporaries’ earlier limitations.

4 releases

0.10.5 Aug 20, 2025
0.10.4 Aug 20, 2025
0.10.2 Jan 21, 2025
0.10.0 Nov 14, 2024

#402 in Programming languages

Download history

165 downloads per month

MIT/Apache

14KB
161 lines

Make temporary values useable in a fairly normal style through macros. This nicely wraps the hacks that work around temporaries’ earlier limitations.

Note

Rust 1.89 has mostly solved these problems. That makes this crate far less useful going forward. Furthermore Edition 2024 had already closed the wormhole magically allowing if_workaround. This still works on newer compilers, but only if the calling code remains on an older Edition.

What Problem does this Solve?

Normally let str = &String::from("str"); shouldn’t compile. The inner String object should be dropped at the end of the statement, leaving a dangling reference. However, in simple cases like this, Rust gives you a pass, by extending the lifetime. Alas this has not yet been generalised. You can often work around that, by storing the temporary value in a separate variable.

The same situation arises, when the temporary value is hidden. Only, then you have no influence to work around it. This is famously the case with format_args!, the efficient alternative to heap-allocated format!. This strongly limits its usefulness – or did, without these macros!

Returning a temporary value from a simple block also benefits from lifetime extension. Again this is not yet the case for more complex blocks, like if or match. Since Rust doesn’t have a ? : operator, that prevents dynamic choice involving temporaries. So, even in edition 2024 these fail to compile. I’m showing both explicit and hidden temporaries in the 1st case:

// `x` & `y` would live longer than the `String` and `format_args`’ hidden temporary.
let x = &vec![&String::from("x")];
let y = format_args!("1 + 2 = {}", 1 + 2);

// Consuming the value directly in `print!()` would be ok, were it not for the man in the middle, `if`.
print!("{}", if 1 < 1 {
    format_args!("1 + 2 = {}", 1 + 2)
} else {
    format_args!("1 + 3 = {}", 1 + 3)
});

The Solution

The let problem is solved by turning it into a match, making the value live as long as the following code. Multiple choice is achieved by breaking from a tuple. Both of these are not nice to look at. Therefore temporaries wraps them up in convenient macros.

This only applies to local processing. Returning a temporary value from a closure or function is alas not something a macro can achieve. That requires a language change, such as super let, which is under discussion.

Actually if_workaround should parse an if statement, but (with sane effort) that is not possible. While if statements put a block after an expression, macro rules oddly do not permit to express this normal Rust syntax.

This was inspired by this & this. There is a similar crate, which, unlike this one, needs a proc-macro, hoist_temporaries.

No runtime deps