that's whole can of different worms, I didn't think that's what the original problem was about. but on a second thought, maybe it's indeed what OP actually asked for.
IMO, this is for sure an XY problem. OP was most likely trying to solve the wrong problem, (or a non-problem at all). I think it's not an alignment problem, but an serialization/deserialization problem. and the packed representation (combined with C representation) is indeed one way to solve similar ser/de problem.
they were probably trying to control the "stride" of data arrays that are passed to the gpu. I'm guessing so, based on the fact that they used the sum of size of the fields to calculate the desired "alignment".
although alignment and array "stride" is related, they are different concepts. alignment of rust types also change the way how rustc generate the CPU code, and packed representation may cause unexpected compile errors about unaligned field references, which could be confusing if the user don't really understand what's going on, especially when implicit reborrows occurred.
so I was hesitating to suggest the "obvious" solution:
#[repr(packed(16))]
pub struct Pair<A, B> {
a: A,
b: B,
}
in reality, this probably has the desired behavior described by OP, but I was under the impression they had misunderstood how alignment (and struct layout, in general) in rust works, and was trying to clarify a bit.
that's why I call it "no smaller" value, not "exact" value, of the alignment.
side note:
the accepted answer with generic_const_exprs also sets a "no smaller" value for the alignment, not "no larger".