常量求值
常量求值是在编译期间计算表达式结果的过程。只有一部分表达式可以在编译时求值。
常量表达式
某些形式的表达式,称为常量表达式,可以在编译时求值。在常量上下文中,这些是唯一允许的表达式,并且总是在编译时求值。在其他地方,例如let 语句中,常量表达式*可能*在编译时求值,但不能保证。如果值必须在编译时求值(即在常量上下文中),则诸如越界数组索引或溢出之类的行为是编译器错误。否则,这些行为是警告,但很可能在运行时出现恐慌。
以下表达式是常量表达式,只要任何操作数也是常量表达式并且不会导致运行任何Drop::drop
调用。
- 字面量.
- 常量参数.
- 指向函数和常量的路径。不允许递归定义常量。
- 指向静态变量的路径。这些只允许在静态变量的初始化器中使用。
- 元组表达式.
- 数组表达式.
- 结构体表达式。
- 块表达式,包括
unsafe
块。 - 字段表达式。
- 索引表达式,使用
usize
的数组索引或切片。 - 范围表达式.
- 不从环境中捕获变量的闭包表达式。
- 用于整数和浮点类型、
bool
和char
的内置否定、算术、逻辑、比较或惰性布尔运算符。 - 共享借用,除非应用于具有内部可变性的类型。
- 除原始指针外的解引用运算符。
- 分组表达式。
- 强制类型转换表达式,除以下情况外
- 指针到地址的强制转换,以及
- 函数指针到地址的强制转换。
- 调用常量函数和常量方法。
- loop、while 和
while let
表达式。 - if、
if let
和 match 表达式。
常量上下文
*常量上下文*是以下情况之一
常量函数
*常量函数 (const fn)* 是允许从常量上下文调用的函数。将函数声明为 const
对任何现有用法都没有影响,它只会限制参数和返回类型可以使用的类型,以及防止在其中使用各种表达式。您可以自由地对常量函数执行与常规函数相同的操作。
当从常量上下文调用时,编译器会在编译时解释该函数。解释发生在编译目标的环境中,而不是主机环境中。因此,如果您针对 32
位系统进行编译,则 usize
为 32
位,而与您是在 64
位还是 32
位系统上构建无关。
常量函数有各种限制,以确保它们可以在编译时求值。例如,不可能将随机数生成器编写为常量函数。在编译时调用常量函数将始终产生与在运行时调用它相同的结果,即使多次调用也是如此。此规则有一个例外:如果您在极端情况下执行复杂的浮点运算,则可能会得到(略微)不同的结果。建议不要使数组长度和枚举判别式依赖于浮点计算。
在常量上下文中允许但在常量函数中不允许的值得注意的功能包括
- 浮点运算
- 浮点值被视为泛型参数,除了
Copy
之外没有特征边界。所以你不能对它们做任何事情,只能复制/移动它们。
- 浮点值被视为泛型参数,除了
相反,以下内容在常量函数中是可能的,但在常量上下文中是不可能的
- 使用泛型类型和生命周期参数。
- 常量上下文确实允许有限地使用常量泛型参数。