Erro E0435 do Rust: attempt to use a non-constant value in a constant
O E0435 quer dizer que algo que precisa ser conhecido enquanto o programa está sendo compilado tenta usar um valor que só existe enquanto ele roda. O lugar mais comum é o tamanho de um array, como [0.0; days], ou um const que lê uma variável let. O código parece totalmente razoável, e em muitas linguagens funcionaria. Em Rust, as constantes e os tamanhos de array são calculados pelo compilador, antes de o programa começar, então eles não podem olhar para variáveis comuns. É um erro muito comum, e a correção costuma ser uma palavra só. Esta página faz parte do guia de erros do compilador.
A mensagem de erro
Seção intitulada “A mensagem de erro”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; |Em português: “tentativa de usar um valor não constante numa constante”. A ajuda sugere usar const em vez de let.
Este é o programa que gera o erro:
fn main() { let days = 7; let rainfall = [0.0; days]; // ❌ an array's length must be known at compile time println!("{:?}", rainfall);}(O comentário quer dizer “o tamanho de um array precisa ser conhecido em tempo de compilação”. Ele fica em inglês porque aparece na saída do compilador.)
O que significa
Seção intitulada “O que significa”O ^^^^ aponta para days na linha 3, dentro de [0.0; days]. Essa é a forma de “repetição” de um array: sete cópias de 0.0. A parte depois do ; é o tamanho do array, e o tamanho faz parte do tipo do array: [f64; 7] e [f64; 8] são dois tipos diferentes. Por isso o rustc precisa saber o número durante a compilação.
Só que days é uma variável let. Para o rustc, um let é um valor que só existe enquanto o programa roda, mesmo quando você escreveu um simples 7 do lado. “Non-constant value” (valor não constante) quer dizer exatamente isso.
A help: mostra a correção: transformar a linha 2 num const. Repare no marcador /* Type */: um const sempre precisa do tipo escrito, então você vai escrever const DAYS: usize = 7;.
A mesma mensagem aparece quando um const ou um static lê uma variável let, como em const PRICE_WITH_TAX: f64 = base_price * 1.2;. Nesse caso, a help: sugere o caminho contrário: usar let em vez de const.
É como imprimir um formulário. O número de quadradinhos do formulário é decidido na gráfica, antes de alguém preenchê-lo. Você não consegue fazer o número de quadradinhos depender de uma resposta que alguém vai escrever no formulário depois.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”O Rust tem dois momentos: o tempo de compilação, quando o cargo build transforma o seu código num programa, e o tempo de execução, quando o programa roda. Um const, um static e o tamanho de um array são todos decididos em tempo de compilação. O compilador escreve esses valores direto dentro do programa. É por isso que eles são rápidos e podem ser usados em qualquer lugar.
Uma variável let pertence ao tempo de execução. O valor dela pode vir de um arquivo, do que o usuário digitou ou de um cálculo que só acontece quando o código roda. O Rust não abre uma exceção para let days = 7; só porque, neste caso, o valor é um literal: a regra continua simples, e o seu código não quebra no dia em que você trocar 7 por read_days_from_config().
Então a pergunta a fazer é: eu já sei esse número quando escrevo o código? Se sim, use um const. Se não, ele não pode ser o tamanho de um array, e um Vec é a ferramenta certa. Veja Constantes e Arrays ou vetores?.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Transforme o tamanho num const
Seção intitulada “Correção 1: Transforme o tamanho num const”Escolha esta quando o número é fixo no seu código, como os 7 dias da semana. Faça o que a help: diz, com duas pequenas mudanças: escreva o tipo (usize para um tamanho) e use um nome em MAIÚSCULAS, que é o estilo do Rust para constantes:
const DAYS: usize = 7; // ✅ conhecido em tempo de compilação
fn main() { let mut rainfall = [0.0; DAYS]; rainfall[2] = 4.5; rainfall[5] = 1.0; println!("{:?}", rainfall); println!("{} dias", rainfall.len());}Saída:
[0.0, 0.0, 4.5, 0.0, 0.0, 1.0, 0.0]7 diasVocê também pode colocar o const dentro de main, bem onde estava o let. No topo do arquivo, todas as funções podem usá-lo. Se você mantiver o nome em minúsculas, days, o código compila, mas com um aviso dizendo que constantes devem ficar em maiúsculas.
Correção 2: Use um Vec quando o tamanho só é conhecido em tempo de execução
Seção intitulada “Correção 2: Use um Vec quando o tamanho só é conhecido em tempo de execução”Escolha esta quando o número vem do programa rodando: o tamanho de uma lista, um valor lido de um arquivo, algo que o usuário digitou, o parâmetro de uma função. Arrays têm tamanho fixo; um Vec é a lista que cresce e pode ter qualquer tamanho. vec![valor; n] é a mesma forma de “repetição”, e aqui n pode ser qualquer variável:
fn main() { let players = ["Ana", "Ben", "Chen"]; let rounds = players.len() * 2;
let mut scores = vec![0; rounds]; // ✅ o tamanho de um Vec pode vir de uma variável scores[0] = 10; scores[3] = 7; println!("{:?}", scores); println!("{} rodadas", scores.len());}Saída:
[10, 0, 0, 7, 0, 0]6 rodadasEsta é a correção certa muito mais vezes do que as pessoas imaginam. Um Vec funciona em quase todo lugar onde um array funciona: indexação, .len(), laços, slices (&scores[1..3]). A diferença de velocidade é mínima em programas normais. Veja Vetores.
Correção 3: Use let em vez de const
Seção intitulada “Correção 3: Use let em vez de const”Escolha esta quando um const (ou static) lê uma variável let. Essa versão do erro fica assim:
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`O programa com erro:
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);}(O comentário quer dizer “um const não pode ler uma variável let”.)
Se o valor é calculado enquanto o programa roda, ele é uma variável comum, então use let (com um nome em minúsculas). Se os dois valores forem mesmo fixos, o outro caminho também funciona: transforme o primeiro num const também. Os dois jeitos aparecem aqui:
const BASE_PRICE: f64 = 20.0;const PRICE_WITH_TAX: f64 = BASE_PRICE * 1.2; // ✅ um const pode usar outro const
fn main() { let base_price = 35.0; let price_with_tax = base_price * 1.2; // ✅ um let pode usar qualquer coisa println!("{:.2}", price_with_tax); println!("{:.2}", PRICE_WITH_TAX);}Saída:
42.0024.00A linha help: sugere let PRICE_WITH_TAX; isso compila, mas você recebe um aviso de que variáveis devem estar em snake_case. Renomeie para price_with_tax.
Correção 4: Const generics ou uma const fn, quando você precisa mesmo de um array
Seção intitulada “Correção 4: Const generics ou uma const fn, quando você precisa mesmo de um array”Escolha esta quando você quer um array de tamanho fixo de verdade, mas o tamanho deve vir de fora da função ou ser calculado. O parâmetro de uma função também é um valor de tempo de execução, então isto dá E0435 (o rustc diz que o parâmetro would need to be a const, precisaria ser um 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 | ^^^^O programa com erro:
fn empty_row(size: usize) { let row = [0u8; size]; // ❌ `size` is only known when the function runs println!("{:?}", row);}
fn main() { empty_row(3);}(O comentário quer dizer “size só é conhecido quando a função roda”.)
Um const generic (parâmetro genérico constante) faz o que o rustc pede: <const N: usize> transforma o tamanho num parâmetro de tempo de compilação, escrito empty_row::<5>() ou deduzido pelo tipo. Uma const fn é uma função que o compilador consegue executar em tempo de compilação, então você pode usá-la para calcular uma constante:
fn empty_row<const N: usize>() -> [u8; N] { [0; N] // ✅ N é um parâmetro de tempo de compilação}
const fn cells(width: usize, height: usize) -> usize { width * height}
const WIDTH: usize = 4;const HEIGHT: usize = 3;const CELLS: usize = cells(WIDTH, HEIGHT); // ✅ calculado pelo compilador
fn main() { let row: [u8; 3] = empty_row(); let wide = empty_row::<5>(); println!("{:?}", row); println!("{:?}", wide);
let board = [0u8; CELLS]; println!("{} células", board.len());}Saída:
[0, 0, 0][0, 0, 0, 0, 0]12 célulasUm limite no Rust stable: dentro de uma função genérica você pode usar N sozinho como tamanho, mas não um cálculo com ele, como [u8; N * 2]. Isso dá generic parameters may not be used in const operations (parâmetros genéricos não podem ser usados em operações constantes) e exige um recurso que só existe no nightly. Se é você quem chama empty_row(size) com um size de tempo de execução, volte para a Correção 2. Veja Genéricos.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Um tamanho vindo de .len() ou do que o usuário digitou. let n = names.len(); let done = [false; n]; dá E0435, porque n só é conhecido em tempo de execução. Correção: let done = vec![false; n]; (Correção 2).
Um static que lê um let. static LINE_WIDTH: usize = width; dá a mesma mensagem, com help: consider using `let` instead of `static` . Um static é decidido em tempo de compilação, igualzinho a um const. Correção: use let, ou transforme width num const também.
Um const dentro de uma função genérica. Em fn bytes<T>() -> usize { const SIZE: usize = std::mem::size_of::<T>(); SIZE }, o const não pode usar T. Esse é outro código, o E0401, e a correção segue a mesma ideia: let size = std::mem::size_of::<T>();.
Erros relacionados
Seção intitulada “Erros relacionados”- E0434: can’t capture dynamic environment in a fn item: a mesma regra de que “itens não enxergam variáveis locais”, para uma
fnescrita dentro de outra função. - E0401: can’t use generic parameters from outer item: uma
fnou umconstaninhado que usa oTda função de fora. - E0015:
cannot call non-const function `args` in constants, quando umconstchama uma função comum, comostd::env::args(), em vez de ler uma variável. Correção:let, ou umaconst fn. - E0308: mismatched types: o que você recebe depois da Correção 1 se um array com o tamanho errado vai para um lugar onde se espera
[f64; DAYS]. - E0658: use of unstable feature: se você recorrer a recursos constantes que só existem no nightly para contornar este erro.
- Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Por que let n = 3; let a = [0; n]; dá E0435, mesmo que n claramente seja 3?
2. O tamanho da sua lista vem de um arquivo que o programa lê. Qual é a correção certa?
3. Qual linha compila?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0435 — Rust error codes index (em inglês): a explicação oficial, com a correção usando
const. - Constants — The Rust Book (em inglês): como
constse diferencia delet, e por que constantes precisam de um tipo. - Constant evaluation — The Rust Reference (em inglês): exatamente quais expressões o compilador consegue calcular em tempo de compilação, e onde elas são exigidas (tamanhos de array,
const,static). - Storing Lists of Values with Vectors — The Rust Book (em inglês): a lista que cresce, para usar quando o tamanho só é conhecido em tempo de execução.
- Const generics MVP — Rust Blog (em inglês): como
<const N: usize>funciona, com exemplos de arrays, e o que o Rust stable consegue e não consegue fazer com isso.