Перейти к содержимому

Ошибка Rust E0435: attempt to use a non-constant value in a constant (не константа)

E0435 значит: то, что должно быть известно во время компиляции программы, пытается использовать значение, которое существует только во время её работы. Чаще всего это длина массива, например [0.0; days], или const, который читает переменную let. Код выглядит совершенно разумно, и во многих языках он бы работал. Но в Rust константы и длины массивов вычисляет компилятор ещё до запуска программы, поэтому обычные переменные им недоступны. Ошибка очень частая, а исправление обычно — одно слово. (Эта страница входит в раздел Ошибки компилятора Rust с объяснениями.)

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;
|

По-русски: «попытка использовать неконстантное значение в константе». help предлагает написать const вместо let.

Вот программа, которая её вызывает (комментарий // ❌ оставлен на английском, потому что rustc цитирует эту строку; он значит «длина массива должна быть известна на этапе компиляции»):

fn main() {
let days = 7;
let rainfall = [0.0; days]; // ❌ an array's length must be known at compile time
println!("{:?}", rainfall);
}

^^^^ указывает на days в строке 3, внутри [0.0; days]. Это «повторяющая» форма массива: семь копий 0.0. Часть после ; — длина массива, а длина входит в тип массива: [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: предлагает обратное: использовать let вместо const.

Это как печать бланка. Сколько клеточек будет на бланке, решают в типографии, до того как кто-то начнёт его заполнять. Нельзя сделать так, чтобы число клеточек зависело от ответа, который кто-то потом впишет в бланк.

В Rust есть два момента: время компиляции, когда cargo build превращает код в программу, и время выполнения, когда программа работает. const, static и длина массива определяются во время компиляции. Компилятор записывает их прямо в программу. Поэтому они быстрые и их можно использовать где угодно.

Переменная let относится ко времени выполнения. Её значение может прийти из файла, из ввода пользователя или из вычисления, которое происходит только при работе кода. Rust не делает исключения для let days = 7; только потому, что здесь случайно стоит литерал: правило остаётся простым, и код не сломается в тот день, когда ты заменишь 7 на read_days_from_config().

Так что задай себе вопрос: знаю ли я это число, когда пишу код? Если да, сделай его const. Если нет, оно не может быть длиной массива, и правильный инструмент — Vec. Подробнее — в разделе Константы и в уроке Массивы.

Выбирай этот вариант, когда число зафиксировано в коде, как 7 дней недели. Сделай, как говорит help:, но с двумя небольшими изменениями: укажи тип (usize для длины) и возьми имя ПРОПИСНЫМИ_БУКВАМИ — так в 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 можно положить и внутрь main, прямо туда, где был let. В начале файла её смогут использовать все функции. Если оставить имя days строчными буквами, код всё равно скомпилируется, но с предупреждением, что константы пишутся в верхнем регистре.

Исправление 2: Используй Vec, когда размер известен только во время работы

Заголовок раздела «Исправление 2: Используй Vec, когда размер известен только во время работы»

Выбирай этот вариант, когда число приходит из работающей программы: длина списка, значение из файла, ввод пользователя, параметр функции. У массивов размер фиксированный, а Vec — растущий список, который может быть любого размера. vec![value; n] — та же «повторяющая» форма, и здесь n может быть любой переменной:

fn main() {
let players = ["Аня", "Бен", "Чэнь"];
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]). Разница в скорости для обычных программ ничтожна. Подробнее — в уроке Векторы.

Выбирай этот вариант, когда 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`

Программа с ошибкой (комментарий значит «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 (с именем строчными буквами). Если оба значения действительно фиксированы, работает и обратный путь: сделай первое значение тоже 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.00
24.00
▶ Попробуй сам

Строка help: предлагает let PRICE_WITH_TAX; это скомпилируется, но ты получишь предупреждение, что переменные пишутся в snake_case. Переименуй её в price_with_tax.

Исправление 4: Константные обобщения или const fn, когда массив действительно нужен

Заголовок раздела «Исправление 4: Константные обобщения или 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 generic) делает то, о чём просит 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
▶ Попробуй сам

Одно ограничение стабильного Rust: внутри обобщённой функции N можно использовать как длину само по себе, но не вычисление с ним, например [u8; N * 2]. Это даёт generic parameters may not be used in const operations и требует возможности, доступной только в nightly. Если empty_row(size) с size времени выполнения вызываешь ты сам, вернись к исправлению 2. Подробнее — в уроке Обобщения.

Размер из .len() или из ввода пользователя. let n = names.len(); let done = [false; n]; даёт E0435, потому что n известен только во время работы. Исправление: let done = vec![false; n]; (исправление 2).

static, который читает let. static LINE_WIDTH: usize = width; даёт тот же заголовок с подсказкой help: consider using `let` instead of `static` . 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>();.

Проверь себя

1. Почему let n = 3; let a = [0; n]; даёт E0435, хотя n явно равно 3?

2. Размер списка берётся из файла, который читает программа. Как правильно исправить?

3. Какая строка скомпилируется?

Результат: 0 / 3