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

Why does Rust check array bounds at runtime, when (most) other checks occur at compile time?

正在阅读basic introduction

If you try to use a subscript that is not in the array, you will get an error: array access is bounds-checked at run-time.

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

因为在一般情况下在编译时检查索引是不可行的。即使对于小程序来说,推理任意变量的可能值也是介于困难和不可能之间的事情。没有人愿意:

  1. 正式证明索引不能越界,并且
  2. 将该证明编码到类型系统中

... 对于 每个 切片/Vec/等等。 access,因为这是你在编译时执行边界检查所必须做的。您基本上需要依赖类型。

除了可能使类型检查变得不可判定(并让程序更难进行类型检查)之外,类型推断通常变得不可能(并且在最好的情况下受到更多限制),类型变得更加复杂和冗长,并且语言的复杂性显着增加。只有在非常简单的情况下,无需大量额外的程序员工作才能证明索引在边界内。

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

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

let a = [1, 2];
let element = a[100];
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

但是,这是有限的,并且可以通过使索引值不是 "obvious" 常量来轻松避免:

let a = [1, 2];
let idx = 100;
let element = a[idx];