为什么 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 在运行时检查数组边界,而大多数其他检查似乎发生在编译时?
因为在一般情况下在编译时检查索引是不可行的。即使对于小程序来说,推理任意变量的可能值也是介于困难和不可能之间的事情。没有人愿意:
- 正式证明索引不能越界,并且
- 将该证明编码到类型系统中
... 对于 每个 切片/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];
正在阅读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 在运行时检查数组边界,而大多数其他检查似乎发生在编译时?
因为在一般情况下在编译时检查索引是不可行的。即使对于小程序来说,推理任意变量的可能值也是介于困难和不可能之间的事情。没有人愿意:
- 正式证明索引不能越界,并且
- 将该证明编码到类型系统中
... 对于 每个 切片/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];