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 const
s 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:
- value_size: compute_value_size::<T>(),
+ value_size: {
+ // Generic `const { … }` pattern:
+ struct GenericConst<T>(*mut Self);
+ impl<T> GenericConst<T> {
+ const VALUE: usize = compute_value_size::<T>();
+ }
+ GenericConst::<T>::SIZE
+ },
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 two value_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!