My understanding is that the following code has undefined behaviour in C++ due to something called "strict aliasing rule".
#include <cstdint>
enum Foo : int16_t {};
void test(Foo& foo) {
reinterpret_cast<int16_t&>(foo) = 42;
}
In particular, a C++ compiler may omit the assignment altogether because it is allowed to assume that the int16_t&
returned by the reinterpret_cast
doesn't point to the same memory as foo
(of type Foo&
) because their types are different.
My question is, does Rust have something akin to the C++ "strict aliasing rule"? In other words, does the following, equivalent Rust code have undefined behaviour?
#[repr(i16)]
enum Foo { Dummy }
unsafe fn test(foo: &mut Foo) {
*std::mem::transmute::<&mut Foo, &mut i16>(foo) = 42;
}
EDIT:
There's an unrelated issue with the above Rust example code in that test
creates a non-existing enum variant. So, here's the same code with a quick fix:
#[repr(i16)]
enum Foo { Dummy, Yummy = 42 }
unsafe fn test(foo: &mut Foo) {
*std::mem::transmute::<&mut Foo, &mut i16>(foo) = 42;
}
My understanding is that general Rust code (as verified by the compiler) of course contains no undefined behavior (barring compiler bugs), and that Rust-the-language does not define any undefined behavior, unlike C.
Naturally, then, the only place undefined behavior could occur is within unsafe
blocks or functions. unsafe
blocks are meant to encapsulate any potentially dangerous behavior as being safe as a whole. A post from July 2014 mentions that Rust's compiler requires that certain invariants are met, and that it is generally dangerous to break those invariants, even in unsafe
blocks (in fact, it is only possible to break then within unsafe
blocks).
One of these dangerous behaviors is pointer aliasing (which seems to be defined in LLVM itself). Interestingly, however, the LLVM docs say (emphasis mine):
Consequently, type-based alias analysis, aka TBAA, aka
-fstrict-aliasing, is not applicable to general unadorned LLVM IR
Thus, it seems that generally, as long as none of the other unsafe behaviors are triggered as a result of the unsafe
block, strict aliasing isn't an issue in Rust.
That being said, your specific example is possibly dangerous, because it seems to match one of the unsafe behaviors from the reference:
Invalid values in primitive types, even in private fields/locals:
- A discriminant in an enum not included in the type definition
I wrote a small extension to your example that shows the binary representations of the enum variants, before and after the unsafe operation: http://is.gd/x0K9kN
As you can see, assigning 42
to the enum value does not match any of the defined discriminator values. If, however, you had assigned either 0
or 1
to the enum value (or instead had defined explicit discriminators for the enum variants), then the operation should theoretically be fine.