为什么Rust会在运行时检查数组边界,何时(大多数)其他检查在编译时发生?

1ij*_*ijk 9 arrays runtime-error compiler-errors language-design rust

阅读基本介绍:

如果您尝试使用不在数组中的下标,则会出现错误:在运行时对数组访问进行边界检查.

为什么Rust在运行时检查数组边界,看起来大多数其他检查在编译时发生?

小智 17

因为在一般情况下在编译时检查索引是不可行的.关于任意变量的可能值的推理介于很难和不可能之间,即使对于小程序也是如此.没人想要:

  1. 正式证明指数不能超出范围,并且
  2. 将证明编码到类型系统中

...对于一片/ Vec/等.访问,因为这是您在编译时执行边界检查所必须执行的操作.你基本上需要依赖打字.

除了可能使类型检查不可判定(并且使程序更难以进行类型检查)之外,类型推断通常变得不可能(并且在最好的情况下更加受限制),类型变得更加复杂和冗长,以及语言的复杂性显着增加.只有在非常简单的情况下,如果没有大量额外的程序员工作,那么这些指数才能被证明.

此外,没有动力摆脱边界检查.生命周期通过几乎完全消除对垃圾收集的需求来减轻它们的重量 - 这是一个巨大的侵入性功能,具有不可预测的吞吐量,空间和延迟影响.另一方面,运行时边界检查非常非侵入性,具有小而众所周知的开销,并且可以在性能关键部分中选择性地关闭,即使程序的其余部分全部使用它也是如此.

请注意,编译器可以数组的越界访问进行一些简单的检查:

let a = [1, 2];
let element = a[100];
Run Code Online (Sandbox Code Playgroud)
error: index out of bounds: the len is 2 but the index is 100
 --> src/main.rs:3:19
  |
3 |     let element = a[100];
  |                   ^^^^^^
  |
  = note: #[deny(const_err)] on by default
Run Code Online (Sandbox Code Playgroud)

但是,这是有限的,并且通过使索引值不是"明显的"常量而容易避免:

let a = [1, 2];
let idx = 100;
let element = a[idx];
Run Code Online (Sandbox Code Playgroud)

  • @ChaseMay Rust知道数组的长度,但在编译时可能并不总是知道索引值,该索引值可以是一个变量,其值是从函数调用返回的。Tuple 的不同之处在于,tuple 的索引实际上是字段名(类似于结构体的字段名),它不能是变量。 (3认同)
  • 建议运行时边界检查是“非常非侵入性的”。影响将与处理阵列的算法的复杂性有关。对于像运行时间这样的度量,本质上为每个数组访问添加边界检查是一个常数乘数。 (2认同)