Programmingシステム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式を使用する必要がありました。