编程系统Rust开发者

Rust中的常量计算是如何工作的(const evaluation)?const与static和let有什么区别,何时可以(以及应该)使用const fn,编写编译时计算时有哪些限制?

用 Hintsage AI 助手通过面试

答案。

Rust中的常量计算允许在编译时执行部分计算或初始化,而不是在程序运行时。

  • const 声明一个不可变的常量,该常量在编译时计算,且没有在内存中的地址:
const PI: f64 = 3.1415;
  • static 声明一个位于特定内存段(通常是.data或.bss)中的全局变量,可能是可变的(需要unsafe):
static mut GLOBAL_COUNTER: i32 = 0;
  • let 用于栈上的变量,其值可以在运行时计算,并且必须在第一次使用之前进行初始化。

const fn 是一种函数,其结果可以用于指定const或static的值。这种函数可以在常量上下文中调用。

const fn factorial(n: usize) -> usize { if n == 0 { 1 } else { n * factorial(n - 1) } } const FACT_5: usize = factorial(5); // 编译通过!

const fn的限制:

  • 只能使用其他const fn,
  • 不能使用堆分配(Box::new等),
  • 不能调用不安全的操作,
  • 无法访问外部变量和函数(除非它们是const fn)。

误导性问题。

问题: 在const上下文中可以使用任何函数,只要其结果不变?例如,像这样:

fn add(a: i32, b: i32) -> i32 { a + b } const RES: i32 = add(1, 2);

典型错误答案: 是的,因为函数是纯粹的,结果是提前已知的。

正确答案: 不,函数必须明确声明为const fn,只有这样它才能在const初始化中被调用。普通函数仅在运行时调用!

示例:

const fn add(a: i32, b: i32) -> i32 { a + b } const RES: i32 = add(1, 2); // 编译通过!

由于对主题细节的不了解而导致的真实错误示例。


故事

在一个三维几何计算项目中,开发者试图通过调用普通函数而不是const fn来声明值表,导致编译错误,并失去了编译时计算的收益。


故事

使用static mut作为全局缓存在多个线程访问时导致数据竞争(static mut是不安全的!)。需要使用Atomic或Mutex来同步对全局资源的访问。


故事

在试图加速大型数组的初始化时,将其定义为static,但忘记了static总是具有固定地址,导致数据未如本地数据一样进入CPU缓存,操作在服务器逻辑的热路径上变得缓慢。需要使用局部let表达式在运行时进行计算。