The following Rust code:

use std::mem;fn main() {#[derive(Debug)]#[repr(C)]struct Test {y: f32,x: u32,}let a = Test { x: 1, y: 1.2 };let y = unsafe { mem::transmute::<Test, u64>(a) };let b = unsafe { mem::transmute::<u64, Test>(y) };println!("{y} {} {}", b.x, b.y);}

Works fine: 5361998234 1 1.2

The equivalent (in my opinion) C++ code

#include <iostream>struct Test {unsigned int x;float y;};int main() {Test a = Test{1, 1.2};long y = *reinterpret_cast<long *>(&a);Test t = *reinterpret_cast<Test *>(&y);std::cout << y << " " << t.x << " " << t.y << std::endl;}

does not: 1 1 1.4013e-45

I've tried all of the following different ways of doing the reinterpret_cast

#include <iostream>struct Test {unsigned int x;float y;};int main() {Test *a = new Test{1, 1.2};long y = *reinterpret_cast<long *>(&a);Test t = *reinterpret_cast<Test *>(&y);std::cout << y << " " << t.x << " " << t.y << std::endl;}
#include <iostream>struct Test {unsigned int x;float y;};int main() {Test *a = new Test{1, 1.2};long y = *reinterpret_cast<long *>(a);Test t = *reinterpret_cast<Test *>(&y);std::cout << y << t.x << " " << t.y << std::endl;}

None of these seem to work, generating different outputs for each.What am I doing wrong? What would be a correct equivalent of the Rust program?

Note: This is for purely recreational purposes, there is no reason for me to do this, I just want to.

2

Best Answer


In C++, reinterpret_cast doesn't relax strict aliasing rules, so it cannot be used for punning arbitrary types. The only transmutation allowed universally is to inspect an object as a sequence of narrow character (char or signed char or unsigned char and/or std::byte).

You should instead use std::memcpy as Richard Critten suggested in a comment. In most cases the compiler will generate the same efficient machine code, but also handle aliasing assumptions correctly.

I'm not sure if it this helps, but I wrote what seems to be a rough equivalent of Rust's std::mem::transmute using a union, based on this reddit post: https://www.reddit.com/r/cpp/comments/1139u3v/equivalent_of_rusts_memforget_for_avoiding/j8qdy1a/

template <class To, class From>std::enable_if_t<sizeof(To) == sizeof(From) && alignof(To) == alignof(From), To>transmute(From from) {union Union {From from;To to;~Union() {}};Union un = {};un.from = std::move(from);return std::move(un.to);}