Custom vtables with integers

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 (:scream:), 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:

- 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! :upside_down_face:

12 Likes