Ошибка 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 так делает
Заголовок раздела «Почему Rust так делает»В Rust есть два момента: время компиляции, когда cargo build превращает код в программу, и время выполнения, когда программа работает. const, static и длина массива определяются во время компиляции. Компилятор записывает их прямо в программу. Поэтому они быстрые и их можно использовать где угодно.
Переменная let относится ко времени выполнения. Её значение может прийти из файла, из ввода пользователя или из вычисления, которое происходит только при работе кода. Rust не делает исключения для let days = 7; только потому, что здесь случайно стоит литерал: правило остаётся простым, и код не сломается в тот день, когда ты заменишь 7 на read_days_from_config().
Так что задай себе вопрос: знаю ли я это число, когда пишу код? Если да, сделай его const. Если нет, оно не может быть длиной массива, и правильный инструмент — Vec. Подробнее — в разделе Константы и в уроке Массивы.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: Сделай размер const
Заголовок раздела «Исправление 1: Сделай размер const»Выбирай этот вариант, когда число зафиксировано в коде, как 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]дней: 7const можно положить и внутрь 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]). Разница в скорости для обычных программ ничтожна. Подробнее — в уроке Векторы.
Исправление 3: Используй let вместо const
Заголовок раздела «Исправление 3: Используй let вместо const»Выбирай этот вариант, когда 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.0024.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>();.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0434: can’t capture dynamic environment in a fn item: то же правило «элементы не видят локальные переменные» для
fn, написанной внутри другой функции. - E0401: can’t use generic parameters from outer item: вложенная
fnилиconstиспользуетTвнешней функции. - E0015:
cannot call non-const function `args` in constants— когдаconstвызывает обычную функцию вродеstd::env::args(), а не читает переменную. Исправление:letилиconst fn. - E0308: mismatched types: что получится после исправления 1, если массив неправильной длины попадёт туда, где ожидается
[f64; DAYS]. - E0658: use of unstable feature: если потянуться за константными возможностями только для nightly, чтобы это обойти.
- Все ошибки компилятора с объяснениями
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Почему let n = 3; let a = [0; n]; даёт E0435, хотя n явно равно 3?
2. Размер списка берётся из файла, который читает программа. Как правильно исправить?
3. Какая строка скомпилируется?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- E0435 — Rust error codes index (на английском): официальное объяснение с исправлением через
const. - Константы — книга «Язык программирования Rust» (перевод на русский): чем
constотличается отletи почему константам нужен тип. - Constant evaluation — The Rust Reference (на английском): какие именно выражения компилятор может вычислить на этапе компиляции и где они обязательны (длины массивов,
const,static). - Хранение списков значений с векторами — книга «Язык программирования Rust» (перевод на русский): растущий список для случаев, когда размер известен только во время работы.
- Const generics MVP — Rust Blog (на английском): как работает
<const N: usize>, с примерами массивов, и что стабильный Rust может и чего не может с этим делать.