Rust 中的严格别名? [英] Strict aliasing in Rust?

查看:63
本文介绍了Rust 中的严格别名?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的理解是,由于所谓的严格别名规则",以下代码在 C++ 中有未定义的行为.

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;
}

特别是,C++ 编译器可能会完全省略赋值,因为它允许假设 reinterpret_cast 返回的 int16_t& 不指向相同的内存作为 foo(属于 Foo& 类型),因为它们的类型不同.

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.

我的问题是,Rust 是否有类似于 C++严格别名规则"的东西?换句话说,以下等效的 Rust 代码是否具有未定义的行为?

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;
}


上面的 Rust 示例代码存在一个不相关的问题,即 test 创建了一个不存在的枚举变体.所以,这里有一个快速修复的相同代码:


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;
}

推荐答案

我的理解是,一般的 Rust 代码(由编译器验证)当然不包含未定义的行为(除非编译器错误),而且 Rust-the-language与 C 不同,没有定义任何未定义的行为.

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.

当然,未定义行为可能发生的唯一地方是unsafe块或函数.unsafe 块旨在将任何潜在的危险行为封装为 整体安全.2014 年 7 月的一篇帖子 提到 Rust 的编译器要求满足某些不变量,并且通常 危险打破那些不变量,即使在 unsafe(实际上,只有在 unsafe 块内才能打破 then).

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).

这些危险行为之一是指针别名(这似乎在 LLVM 本身中定义).然而,有趣的是,LLVM 文档说(强调我的):

One of these dangerous behaviors is pointer aliasing (which seems to be defined in LLVM itself). Interestingly, however, the LLVM docs say (emphasis mine):

因此,基于类型的别名分析,又名 TBAA,又名-fstrict-aliasing,不适用于普通的 LLVM IR

Consequently, type-based alias analysis, aka TBAA, aka -fstrict-aliasing, is not applicable to general unadorned LLVM IR

因此,似乎一般来说,只要 unsafe 块没有触发其他不安全行为,严格别名在 Rust 中就不是问题.

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:

  • 类型定义中未包含的枚举中的判别式

我为您的示例编写了一个小扩展,显示了在不安全操作之前和之后枚举变体的二进制表示:http://is.gd/x0K9kN

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

如您所见,将 42 分配给枚举值与任何定义的鉴别器值都不匹配.但是,如果您已将 01 分配给枚举值(或者为枚举变体定义了显式鉴别器),那么理论上该操作应该没问题.

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.

这篇关于Rust 中的严格别名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆