error[E0515]: cannot return reference to temporary value
--> src/lib.rs:9:5
|
9 | &Vtable {
| ______^-
| | _____|
| ||
10 | || to_string: to_string::<T>,
11 | || value_size: compute_value_size::<T>(),
12 | || }
| || ^
| ||_____|
| |______returns a reference to data owned by the current function
| temporary value created here
Is there any way to get around static promotion no longer working in this case? Curiously, if you call std::mem::size_of directly rather than call my custom function, then it does work, but I don't understand why.
The actual value I want to put in my vtable is the offset of a field in the struct.
On the other hand it makes sense that there are limitations beyond what that RFC wrote. You probably wouldn't want
let x: &u32 = &(panic!() as _);
to panic at compile time, even though
static X: &'static u32 = &(panic!() as _);
compiles somewhat successfully (at least until it reports that compile-time panic).
And even more non-trivial than simple panics, non-terminating (or slow) computations would be hard to handle in non-confusing ways. (E. g.: Should static promotion be tried and then disregarding it becomes a silent fallback in case of panic or if evaluation is taking to long? But if hitting some evaluation limit turns into lifetime errors that would be weird and confusing.)
Yes, this last point from @steffahn hits the nail on the head: traditionally code used to be more aggressive with static promotion, but this incurred in problems and limitations, related to UB-in-function-or-computation being lifted to compile-time (), and the semver hazards Steffahn mentioned about introducing panics in the function-or-computation causing compile-errors.
To illustrate:
use ::core::num::NonZeroU8;
if N != 0 {
let x: &u8 = 42 / N; // compile-error if lifted to compile-time!
unsafe {
let y: &NonZeroU8 = &NonZeroU8::new_unchecked(N); // UB if lifted to compile-time
}
}
Using explicit consts thus makes it clear that the author intended for the calculation / function-call to occur at compile-time, which thus makes these branched / guarded scenarios impossible. Hence why a more ad-hoc solution to the OP would be to involve a helper trait just for the problematic value:
that being said, for the specific case of a vtable, using a "generic const" for the whole vtable value will scale better (e.g., imagine there being twovalue_size kind of vtable entries).
Aside: FWIW, I had always been using the generic const approach for vtables, since I had a fuzzy memory of inline versions of it not being static-promoted for one reason or another. It's only somewhat recently that somebody "proved me wrong" with vtables of only function pointers, which puzzled me quite a bit since I did remember some kind of failure about it. So this thread finally clarifies things: not all of the const-compatible values are eligible for static promotion after all!
With the explicit form, it fixes the problems mentioned above in Custom vtables with integers - #4 by steffahn -- if you want something more complex than something "obviously" fine like &10, you have to say that you need it done at compile-time, rather than leaving it up to the optimizer.
…as far as I understand, const (as in, “can be used as the value of a const”) is about two properties of a value:
can be evaluated at compile-time
can be duplicated using a shallow copy (the concrete value can be duplicated, not any value of the same type)
With this in mind, for the use-case of array expressions, I wonder if one couldn’t rely solely on this second property while leaving the evaluation to run-time. I.e. the same checks as for const expressions is used to determine whether its okay to duplicate the value, however the problems of non-termination or panics can be left to run-time. (const { … } would still be useful to force compiler-time evaluation.)
(Of course for rvalue promotion, compile-time evaluation is essential, so this wouldn’t matter for that use-case.)