Rustのエラー E0435: attempt to use a non-constant value in a constant(定数に定数でない値を使った)
E0435 は、コンパイルの時点でわかっていなければならないものが、プログラムの実行中にしか存在しない値を使おうとした、という意味のエラーです。よくあるのは [0.0; days] のような配列の長さや、let の変数を読む const です。コードはまったく自然に見えますし、ほかの多くの言語なら動くでしょう。でも Rust では、定数と配列の長さはプログラムが動き出す前にコンパイラが計算するので、ふつうの変数を見ることができません。とてもよくあるエラーで、直すのはたいてい単語1つです。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0435]: attempt to use a non-constant value in a constant --> src/main.rs:3:26 |3 | let rainfall = [0.0; days]; // ❌ an array's length must be known at compile time | ^^^^ non-constant value |help: consider using `const` instead of `let` |2 - let days = 7;2 + const days: /* Type */ = 7; |「attempt to use a non-constant value in a constant」は「定数の中で、定数でない値を使おうとした」という意味です。
このエラーが出るのは、次のプログラムです。
fn main() { let days = 7; let rainfall = [0.0; days]; // ❌ an array's length must be known at compile time println!("{:?}", rainfall);}コメントは「配列の長さはコンパイル時にわかっていなければならない」という意味です(エラーになる例なので、コードはそのままにしています)。
エラーの意味
Section titled “エラーの意味”^^^^ は、3行目の [0.0; days] の中の days を指しています。これは配列の「繰り返し」の書き方で、0.0 を7つ並べるという意味です。; のあとの部分が配列の長さで、長さは配列の型の一部です。[f64; 7] と [f64; 8] は別々の型なのです。だから rustc は、コンパイルしている間にその数を知っていなければなりません。
ところが days は let の変数です。rustc にとって let は、プログラムの実行中にしか存在しない値です。すぐ横にただの 7 と書いてあっても同じです。「non-constant value」(定数でない値)とは、まさにこのことです。
help: が直し方を教えてくれています。2行目を const にしなさい、ということです。/* Type */ という仮の文字に注目してください。const には必ず型を書く必要があるので、実際には const DAYS: usize = 7; と書きます。
const や static が let の変数を読んだときにも、同じ見出しのエラーが出ます。たとえば const PRICE_WITH_TAX: f64 = base_price * 1.2; です。このときの help: は逆向きで、const の代わりに let を使うように提案します。
用紙の印刷にたとえてみましょう。用紙の記入欄の数は、だれかが記入する前に、印刷所で決まります。あとで用紙に書き込まれる答えによって、記入欄の数を変えることはできませんよね。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust には2つのタイミングがあります。cargo build がコードをプログラムに変えるコンパイル時と、プログラムが動いている実行時です。const、static、そして配列の長さは、すべてコンパイル時に決まります。コンパイラはそれらをプログラムに直接書き込みます。だから速いですし、どこでも使えるのです。
let の変数は実行時のものです。その値は、ファイルから来るかもしれませんし、ユーザーの入力や、コードが動いてはじめて行われる計算から来るかもしれません。let days = 7; はたまたまリテラルだから、といって Rust が例外を作ることはありません。そうすればルールはシンプルなままですし、いつか 7 を read_days_from_config() に変えた日に、コードが壊れることもありません。
ですから、自分にこう問いかけてみてください。この数は、コードを書いている時点でわかっているだろうか? わかっているなら const にします。わからないなら、それは配列の長さにはできないので、Vec が正しい道具です。定数と配列とベクタの使い分けを見てください。
直し方1:サイズを const にする
Section titled “直し方1:サイズを const にする”1週間の7日のように、数がコードの中で決まっているときは、これを選びます。help: のとおりにしますが、2つだけ変えます。型を書くこと(長さなら usize)と、名前を大文字にすることです。大文字の名前(UPPER_CASE)は、Rust の定数のスタイルです。
const DAYS: usize = 7; // ✅ コンパイル時にわかっている
fn main() { let mut rainfall = [0.0; DAYS]; rainfall[2] = 4.5; rainfall[5] = 1.0; println!("{:?}", rainfall); println!("{}日分", rainfall.len());}実行結果:
[0.0, 0.0, 4.5, 0.0, 0.0, 1.0, 0.0]7日分const は、let があった場所、つまり main の中に置いてもかまいません。ファイルのいちばん上に置けば、どの関数からも使えます。小文字の days という名前のままでもコンパイルはできますが、定数は大文字にするべきだという警告が出ます。
直し方2:サイズが実行時にしかわからないなら Vec を使う
Section titled “直し方2:サイズが実行時にしかわからないなら Vec を使う”リストの長さ、ファイルから読んだ値、ユーザーの入力、関数の引数のように、数が動いているプログラムから来るときは、これを選びます。配列のサイズは固定ですが、Vec はどんなサイズにもなれる、伸び縮みするリストです。vec![value; n] は同じ「繰り返し」の書き方で、こちらの n にはどんな変数でも使えます。
fn main() { let players = ["Ana", "Ben", "Chen"]; let rounds = players.len() * 2;
let mut scores = vec![0; rounds]; // ✅ Vec の長さは変数で決めてよい scores[0] = 10; scores[3] = 7; println!("{:?}", scores); println!("{}ラウンド", scores.len());}実行結果:
[10, 0, 0, 7, 0, 0]6ラウンドこれが正しい直し方であることは、思っているよりずっと多いです。Vec は、配列が使えるほとんどの場所で使えます。インデックス、.len()、ループ、スライス(&scores[1..3])などです。ふつうのプログラムなら、速さの違いはごくわずかです。ベクタを見てください。
直し方3:const の代わりに let を使う
Section titled “直し方3:const の代わりに let を使う”const(や static)が let の変数を読んでいるときは、これを選びます。この場合のエラーは次のようになります。
error[E0435]: attempt to use a non-constant value in a constant --> src/main.rs:3:33 |3 | const PRICE_WITH_TAX: f64 = base_price * 1.2; // ❌ a `const` can't read a `let` variable | ^^^^^^^^^^ non-constant value |help: consider using `let` instead of `const`help: は「const の代わりに let を使うことを検討してください」という意味です。
エラーになるプログラムです(コメントは「const は let の変数を読めない」という意味です)。
fn main() { let base_price = 20.0; const PRICE_WITH_TAX: f64 = base_price * 1.2; // ❌ a `const` can't read a `let` variable println!("{:.2}", PRICE_WITH_TAX);}値がプログラムの実行中に計算されるなら、それはふつうの変数です。だから let にします(名前は小文字にします)。両方の値が本当に決まっているなら、逆のやり方でもかまいません。1つ目の値も const にするのです。次の例では、両方を見せています。
const BASE_PRICE: f64 = 20.0;const PRICE_WITH_TAX: f64 = BASE_PRICE * 1.2; // ✅ const はほかの const を使える
fn main() { let base_price = 35.0; let price_with_tax = base_price * 1.2; // ✅ let は何でも使える println!("{:.2}", price_with_tax); println!("{:.2}", PRICE_WITH_TAX);}実行結果:
42.0024.00help: の行は let PRICE_WITH_TAX を提案します。これでもコンパイルはできますが、変数は snake_case にするべきだという警告が出ます。price_with_tax に名前を変えましょう。
直し方4:本当に配列が必要なら、const ジェネリクスか const fn を使う
Section titled “直し方4:本当に配列が必要なら、const ジェネリクスか const fn を使う”本物の固定サイズの配列がほしいけれど、そのサイズを関数の外から渡したい、あるいは計算で求めたいときは、これを選びます。関数の引数も実行時の値なので、次のコードは E0435 になります(rustc は、引数が would need to be a const、つまり「const である必要がある」と言っています)。
error[E0435]: attempt to use a non-constant value in a constant --> src/main.rs:2:21 |1 | fn empty_row(size: usize) { | ---- this would need to be a `const`2 | let row = [0u8; size]; // ❌ `size` is only known when the function runs | ^^^^エラーになるプログラムです(コメントは「size は関数が動いたときにはじめてわかる」という意味です)。
fn empty_row(size: usize) { let row = [0u8; size]; // ❌ `size` is only known when the function runs println!("{:?}", row);}
fn main() { empty_row(3);}const ジェネリクス(const generics)を使うと、rustc の言うとおりにできます。<const N: usize> でサイズをコンパイル時のパラメータにして、empty_row::<5>() と書くか、型から推論させます。const fn はコンパイラがコンパイル時に実行できる関数なので、定数を計算するのに使えます。
fn empty_row<const N: usize>() -> [u8; N] { [0; N] // ✅ N はコンパイル時のパラメータ}
const fn cells(width: usize, height: usize) -> usize { width * height}
const WIDTH: usize = 4;const HEIGHT: usize = 3;const CELLS: usize = cells(WIDTH, HEIGHT); // ✅ コンパイラが計算する
fn main() { let row: [u8; 3] = empty_row(); let wide = empty_row::<5>(); println!("{:?}", row); println!("{:?}", wide);
let board = [0u8; CELLS]; println!("{}マス", board.len());}実行結果:
[0, 0, 0][0, 0, 0, 0, 0]12マスstable の Rust には制限が1つあります。ジェネリックな関数の中では、N そのものは長さに使えますが、[u8; N * 2] のように N を使った計算はできません。これは generic parameters may not be used in const operations(ジェネリックパラメータは定数の演算には使えない)というエラーになり、nightly 専用の機能が必要です。実行時の size で empty_row(size) を呼んでいるのが自分なら、直し方2に戻りましょう。ジェネリクスを見てください。
よく出会う場面
Section titled “よく出会う場面”.len() やユーザーの入力から来たサイズ。 let n = names.len(); let done = [false; n]; は E0435 になります。n は実行時にしかわからないからです。直し方:let done = vec![false; n]; にします(直し方2)。
let を読む static。 static LINE_WIDTH: usize = width; も同じ見出しのエラーになり、help: consider using `let` instead of `static` (static の代わりに let を使うことを検討してください)が出ます。static も const と同じく、コンパイル時に決まるからです。直し方:let を使うか、width も const にします。
ジェネリックな関数の中の const。 fn bytes<T>() -> usize { const SIZE: usize = std::mem::size_of::<T>(); SIZE } では、const が T を使えません。これは別のコードの E0401 ですが、直し方の考え方は同じです。let size = std::mem::size_of::<T>(); と書きます。
関連するエラー
Section titled “関連するエラー”- E0434:can’t capture dynamic environment in a fn item(fn アイテムは動的な環境をキャプチャできない)。「アイテムからはローカル変数が見えない」という同じルールで、関数の中に書いた
fnの場合です。 - E0401:can’t use generic parameters from outer item(外側のアイテムのジェネリックパラメータは使えない)。入れ子の
fnやconstが、外側の関数のTを使ったときです。 - E0015:
cannot call non-const function `args` in constants(定数の中で const でない関数argsは呼べない)。constが変数を読む代わりに、std::env::args()のようなふつうの関数を呼んだときです。直し方:letにするか、const fnを使います。 - E0308:mismatched types(型の不一致)。直し方1のあとで、
[f64; DAYS]が求められている場所に違う長さの配列を渡すと、このエラーになります。 - E0658:use of unstable feature(不安定な機能の使用)。このエラーを避けようとして、nightly 専用の const の機能に手を出したときに出ます。
- コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. let n = 3; let a = [0; n]; は、n が 3 なのは明らかなのに、なぜ E0435 になるのですか?
2. リストのサイズは、プログラムが読み込むファイルから決まります。正しい直し方はどれですか?
3. コンパイルできるのはどれですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0435 — Rust error codes index(英語):このエラーの公式の説明です。
constを使う直し方が載っています。 - 変数と可変性 — The Rust Programming Language 日本語版:
constとletの違いと、定数に型が必要な理由を説明している章です。 - Constant evaluation — The Rust Reference(英語):コンパイラがコンパイル時に計算できる式と、それが必要になる場所(配列の長さ、
const、static)の正確な説明です。 - ベクタで値のリストを保持する — The Rust Programming Language 日本語版:サイズが実行時にしかわからないときに使う、伸び縮みするリストです。
- Const generics MVP — Rust Blog(英語):
<const N: usize>の仕組みを配列の例で説明し、stable の Rust でできることとできないことをまとめています。