Why would you want to replace it in the first place? Reading from a differently-typed field of a union has the same effect as transmute(), it's just more code.
This is also discussed in a recent Rust blog post:
this operation is not supported: the transmute above is trying to reinterpret the memory address &() as an integer of type usize . The compiler cannot predict what memory address the () would be associated with at execution time, so it refuses to allow that reinterpretation.
...
You might well be wondering at this point: "Wait, when is it okay to transmute a pointer to a usize during const evaluation?" And the answer is simple: "Never."
Transmuting a pointer to a usize during const-eval has always been undefined behavior, ever since const-eval added support for transmute and union.
It's a bit sad that transmute (or your union trick) can be used to transmute between pointers and integers at compile time (and it's not a compile-time errors like in C++) but it's point where Rust is worse than C++: certain undefined behaviours are not detected.
C++ does better: undefined behaviour doesn't exist in complie-time C++! You can not convert between pointers and integers in compile-time C++ (and it's UB to do that in compiler-time Rust) because in compile-time calculations you don't know the exact address of anything! And only these calculations which don't depend on actual addresses of variables should be allowed. Conversion between pointers and integers opens the pandorra box: now either compiler have to track values which originate from pointers or it have to forbid conversion between integers and pointers.
The fact that unsafe Rust have UB even in const evaluation is a bit worrying: why was it done that way? Even const C++ doesn't have UB but Rust does! Scary. And not sure why it's needed: const evaluations are not full language, just something so simple that you can do that during compile-time, why include UB there?
The blog post is the result of ptr-int transmutes being upgraded from silent UB to a compiler error.
It is a goal that all UB at const time is caught, but it is not computationally feasible to guarantee that all UB is caught. Plus, we're not even sure exactly what the dynamic borrowing rules for pointers are, so we can't exactly guarantee there's no UB there. C++ doesn't have to worry about that.
The biggest case of undiagnosed UB will be access out of bounds of references (e.g. slice::get_unchecked_mut) but which are still in bounds of the underlying allocation. This is a source of UB which is entirely non-existent in C++.
C++ also has the advantage of TBAA and typed memory, whereas Rust's memory is fully untyped and only copies are typed.
I suspect that rustc's const engine does catch every bit of UB that constexpr C++ can run into, and only allows through the "spicy Rust specific UB" resulting from the dynamic borrow rules.