You seem to be assuming new will give you [0, 0, align] in some order, but it could give you something else; we've already seen capacity() need not reflect the value stored (and doesn't for ZSTs), and I'm not aware of any guarantees for the exact value of the pointer.
E.g. the (len, cap, ptr, alignment) is (0, 0, usize::MAX, 1), unique is not unique but is equal to align + 1 (when overflow doesn't panic).
Yeah I changed it to
// Use ManuallyDrop to avoid dropping non-existing elements caused by `set_len`.
let mut vec: Vec<std::mem::MaybeUninit<T>> =
unsafe { std::mem::transmute(Vec::<T>::new()) };
// Should be a no-op, but just in case Vec's internals require this.
vec.reserve(1);
let before: [usize; 3] = unsafe { std::mem::transmute_copy(&vec) };
// SAFETY:
// - T is zero-sized and the Vec's drop won't be called, so we can set the len to anything
unsafe {
vec.set_len(1);
}
let after: [usize; 3] = unsafe { std::mem::transmute_copy(&vec) };
match std::array::from_fn(|i| after[i] - before[i]) {
[1, 0, 0] => 0,
[0, 1, 0] => 1,
[0, 0, 1] => 2,
_ => unreachable!(),
}
which is based on the assumption that only the len field will change when calling set_len. I suppose the sub and [1, 0, 0] can be relaxed to XOR and [_, 0, 0] to accept any change/encoding.
EDIT: the non-ZST impl assumes that the len in set_len(0) is stored as-is, but I don't see a way to measure a change if len may be 0 to begin with, the ptr must always be valid for len elements and reserve may not do anything.
Published under split-spare should anyone be interested.