Pular para o conteúdo

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.

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 ^^^^ 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.

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?.

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 dias
▶ Experimente você mesmo

Você 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 rodadas
▶ Experimente você mesmo

Esta é 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.

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.00
24.00
▶ Experimente você mesmo

A 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élulas
▶ Experimente você mesmo

Um 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.

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>();.

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