我的理解是,由于名为"严格别名规则"。
,以下代码在C ++中具有未定义的行为。#include <cstdint>
enum Foo : int16_t {};
void test(Foo& foo) {
reinterpret_cast<int16_t&>(foo) = 42;
}
特别是,C ++编译器可能会完全省略赋值,因为允许假设int16_t&
返回的reinterpret_cast
与foo
指向的内存不同(类型为Foo&
),因为它们的类型不同。
我的问题是,Rust是否有类似于C ++&#34;严格别名规则&#34;?换句话说,下面的等效Rust代码是否有未定义的行为?
#[repr(i16)]
enum Foo { Dummy }
unsafe fn test(foo: &mut Foo) {
*std::mem::transmute::<&mut Foo, &mut i16>(foo) = 42;
}
修改
上面的Rust示例代码存在一个不相关的问题,test
创建了一个不存在的枚举变体。所以,这里有相同的代码快速修复:
#[repr(i16)]
enum Foo { Dummy, Yummy = 42 }
unsafe fn test(foo: &mut Foo) {
*std::mem::transmute::<&mut Foo, &mut i16>(foo) = 42;
}
答案 0 :(得分:7)
我的理解是,一般Rust代码(由编译器验证)当然不包含未定义的行为(禁止编译器错误),并且Rust-the-language没有定义任何未定义的行为,与C不同。
然后,当然,可能发生的唯一未定义行为位于unsafe
块或函数内。 unsafe
块旨在将任何潜在危险行为封装为being safe as a whole。 A post from July 2014提到Rust的编译器要求满足某些不变量,并且它通常是dangerous to break those invariants, even in unsafe
blocks(事实上,它只能在unsafe
块内断开)。
这些危险行为之一是pointer aliasing(似乎在LLVM本身中定义)。然而,有趣的是,LLVM文档说(强调我的):
因此,基于类型的别名分析,又名TBAA,又名 -fstrict-aliasing,不适用于一般未加修饰的LLVM IR
因此,似乎通常情况下,只要unsafe
块没有触发其他任何不安全行为,严格别名就不会成为Rust中的问题。
话虽如此,您的具体示例可能很危险,因为它似乎与参考中的一个不安全行为相匹配:
原始类型中的值无效,即使在私有字段/本地中也是如此:
- 未包含在类型定义中的枚举中的判别式
我为您的示例编写了一个小扩展,在不安全操作之前和之后显示枚举变体的二进制表示:http://is.gd/x0K9kN
如您所见,将42
分配给枚举值与任何已定义的鉴别器值都不匹配。但是,如果您已将0
或1
分配给枚举值(或者已经为枚举变量定义了显式鉴别符),那么理论上该操作应该没问题。