常量求值

const_eval.md
commit: 8425f5bad3ac40e807e3f75f13b989944da28b62
本章译文最后维护日期:2021-4-5

常量求值是在编译过程中计算[表达式][expressions]结果的过程。(不是所有表达式都可以在编译时求值,也就是说)只有全部表达式的某个子集可以在编译时求值。

常量表达式

某些形式的表达式(被称为常量表达式)可以在编译时求值。在常量(const)上下文中,常量表达式是唯一允许的表达式,并且总是在编译时求值。在其他地方,比如 let语句,常量表达式可以在编译时求值,但不能保证总能在此时求值。如果值必须在编译时求得(例如在常量上下文中),则像数组索引越界或溢出这样的行为都是编译错误。如果不是必须在编译时求值,则这些行为在编译时只是警告,但它们在运行时可能会触发 panic。

下列表达式中,只要它们的所有操作数都是常量表达式,并且求值/计算不会引起任何 Drop::drop函数的运行,那这些表达式就是常量表达式。

常量上下文

下述位置是常量上下文

常量函数

*常量函数(const fn)*可以在常量上下文中调用。给一个函数加一个常量(const)标志对该函数的任何现有的使用都没有影响,它只限制参数和返回可以使用的类型,并防止在这两个位置上使用不被允许的表达式类型。程序员可以自由地用常量函数去做任何用常规函数能做的事情。

当从常量上下文中调用这类函数时,编译器会在编译时解释该函数。这种解释发生在编译目标环境中,而不是在当前主机环境中。因此,如果是针对一个 32 位目标系统进行编译,那么 usize 就是 32 位,这与在一个 64 位还是在一个 32 位主机环境中进行编译动作无关。

常量函数有各种限制以确保其可以在编译时可被求值。因此,例如,不可以将随机数生成器编写为常量函数。在编译时调用常量函数将始终产生与运行时调用它相同的结果,即使多次调用也是如此。这个规则有一个例外:如果在极端情况下执行复杂的浮点运算,那么可能得到(非常轻微)不同的结果。建议不要让数组长度和枚举判别值/式依赖于浮点计算。

常量上下文有,但常量函数不具备的显著特性有:

  • 浮点运算
    • (在常量函数中,)处理浮点值就像处理只有 Copy trait约束的泛型参数一样,不能用它们做任何事,只能复制/移动它们。
  • trait对象(dyn Trait)/动态分发类型
  • 泛型参数上除 Sized 之外的泛型约束
  • 比较裸指针
  • 访问联合体字段
  • 调用 transmute

相反,以下情况在常量函数中是有可能的,但在常量上下文中则不可能: