Ir al contenido

Error E0435 de Rust: attempt to use a non-constant value in a constant

E0435 significa que algo que tiene que conocerse mientras se compila tu programa intenta usar un valor que solo existe mientras se ejecuta. El sitio típico es la longitud de un array, como [0.0; days], o un const que lee una variable let. El código parece de lo más razonable, y en muchos lenguajes funcionaría. En Rust, las constantes y las longitudes de los arrays las calcula el compilador, antes de que el programa arranque, así que no pueden mirar variables normales. Es un error muy común, y casi siempre se arregla cambiando una palabra. Forma parte de la guía de errores del 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;
|

“Attempt to use a non-constant value in a constant” quiere decir “intento de usar un valor no constante en una constante”.

Este es el programa que lo produce:

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

Los ^^^^ señalan days en la línea 3, dentro de [0.0; days]. Esa es la forma de “repetición” de un array: siete copias de 0.0. Lo que va después del ; es la longitud del array, y la longitud forma parte del tipo del array: [f64; 7] y [f64; 8] son dos tipos distintos. Por eso rustc necesita conocer el número mientras compila.

Pero days es una variable let. Para rustc, un let es un valor que solo existe mientras el programa se ejecuta, aunque hayas escrito un simple 7 al lado. Eso es exactamente lo que significa “non-constant value” (valor no constante).

El help: muestra la solución: convertir la línea 2 en un const. Fíjate en el marcador /* Type */: un const siempre necesita su tipo escrito, así que escribirás const DAYS: usize = 7;.

El mismo titular aparece cuando un const o un static lee una variable let, como en const PRICE_WITH_TAX: f64 = base_price * 1.2;. En ese caso, el help: sugiere lo contrario: usar let en lugar de const.

Es como imprimir un formulario. El número de casillas se decide en la imprenta, antes de que nadie lo rellene. No puedes hacer que el número de casillas dependa de una respuesta que alguien escribirá más tarde en el formulario.

Rust tiene dos momentos: el tiempo de compilación, cuando cargo build convierte tu código en un programa, y el tiempo de ejecución, cuando el programa se ejecuta. Un const, un static y la longitud de un array se deciden en tiempo de compilación. El compilador los escribe directamente dentro del programa. Por eso son rápidos y se pueden usar en cualquier sitio.

Una variable let pertenece al tiempo de ejecución. Su valor puede venir de un archivo, de lo que escribe el usuario o de un cálculo que solo ocurre cuando el código se ejecuta. Rust no hace una excepción con let days = 7; solo porque esta vez sea un literal: la regla sigue siendo sencilla, y tu código no se rompe el día que cambies 7 por read_days_from_config().

Así que la pregunta es: ¿conozco este número cuando escribo el código? Si la respuesta es sí, hazlo un const. Si es no, no puede ser la longitud de un array, y la herramienta adecuada es un Vec. Mira Constantes y Arrays frente a vectores.

Solución 1: Haz que el tamaño sea un const

Sección titulada «Solución 1: Haz que el tamaño sea un const»

Elige esta cuando el número está fijo en tu código, como los 7 días de una semana. Haz lo que dice el help:, con dos pequeños cambios: escribe el tipo (usize para una longitud) y usa un nombre en MAYÚSCULAS, que es el estilo de Rust para las constantes:

const DAYS: usize = 7; // ✅ se conoce al compilar
fn main() {
let mut rainfall = [0.0; DAYS];
rainfall[2] = 4.5;
rainfall[5] = 1.0;
println!("{:?}", rainfall);
println!("{} días", rainfall.len());
}

Salida:

[0.0, 0.0, 4.5, 0.0, 0.0, 1.0, 0.0]
7 días
▶ Pruébalo tú mismo

También puedes poner el const dentro de main, justo donde estaba el let. Al principio del archivo, todas las funciones pueden usarlo. Si mantienes el nombre en minúsculas days, compila igual, pero con un aviso (warning) de que las constantes deberían ir en mayúsculas.

Solución 2: Usa un Vec cuando el tamaño solo se conoce en tiempo de ejecución

Sección titulada «Solución 2: Usa un Vec cuando el tamaño solo se conoce en tiempo de ejecución»

Elige esta cuando el número viene del programa en marcha: la longitud de una lista, un valor de un archivo, lo que escribe el usuario, un parámetro de una función. Los arrays tienen un tamaño fijo; un Vec es la lista que puede crecer y tener cualquier tamaño. vec![valor; n] es la misma forma de “repetición”, y aquí n puede ser cualquier variable:

fn main() {
let players = ["Ana", "Ben", "Chen"];
let rounds = players.len() * 2;
let mut scores = vec![0; rounds]; // ✅ la longitud de un Vec puede venir de una variable
scores[0] = 10;
scores[3] = 7;
println!("{:?}", scores);
println!("{} rondas", scores.len());
}

Salida:

[10, 0, 0, 7, 0, 0]
6 rondas
▶ Pruébalo tú mismo

Esta es la solución correcta muchas más veces de lo que la gente cree. Un Vec funciona casi en todos los sitios donde funciona un array: índices, .len(), bucles, slices (&scores[1..3]). La diferencia de velocidad es mínima en programas normales. Mira Vectores.

Elige esta cuando un const (o un static) lee una variable let. Esta versión del error se ve así:

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`

El help: sugiere usar let en lugar de const. El programa con el error:

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

Si el valor se calcula mientras el programa se ejecuta, es una variable normal, así que conviértelo en un let (con un nombre en minúsculas). Si los dos valores son realmente fijos, también funciona al revés: haz que el primero sea también un const. Aquí tienes las dos formas:

const BASE_PRICE: f64 = 20.0;
const PRICE_WITH_TAX: f64 = BASE_PRICE * 1.2; // ✅ un const puede usar otro const
fn main() {
let base_price = 35.0;
let price_with_tax = base_price * 1.2; // ✅ un let puede usar cualquier cosa
println!("{:.2}", price_with_tax);
println!("{:.2}", PRICE_WITH_TAX);
}

Salida:

42.00
24.00
▶ Pruébalo tú mismo

El help: sugiere let PRICE_WITH_TAX; eso compila, pero recibirás un aviso de que las variables deberían ir en snake_case. Cámbiale el nombre a price_with_tax.

Solución 4: Const generics o una const fn, cuando de verdad necesitas un array

Sección titulada «Solución 4: Const generics o una const fn, cuando de verdad necesitas un array»

Elige esta cuando quieres un array de tamaño fijo de verdad, pero el tamaño debe venir de fuera de una función, o calcularse. Un parámetro de una función también es un valor de tiempo de ejecución, así que esto da E0435 (rustc dice que el parámetro would need to be a const, “tendría que ser un 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
| ^^^^

El programa con el error:

fn empty_row(size: usize) {
let row = [0u8; size]; // ❌ `size` is only known when the function runs
println!("{:?}", row);
}
fn main() {
empty_row(3);
}

Un const generic (genérico constante) hace lo que pide rustc: <const N: usize> convierte el tamaño en un parámetro de tiempo de compilación, que se escribe empty_row::<5>() o se deduce del tipo. Una const fn es una función que el compilador puede ejecutar al compilar, así que puedes usarla para calcular una constante:

fn empty_row<const N: usize>() -> [u8; N] {
[0; N] // ✅ N es un parámetro de tiempo de compilació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); // ✅ lo calcula el compilador
fn main() {
let row: [u8; 3] = empty_row();
let wide = empty_row::<5>();
println!("{:?}", row);
println!("{:?}", wide);
let board = [0u8; CELLS];
println!("{} casillas", board.len());
}

Salida:

[0, 0, 0]
[0, 0, 0, 0, 0]
12 casillas
▶ Pruébalo tú mismo

Un límite en Rust estable: dentro de una función genérica puedes usar N solo como longitud, pero no un cálculo con él, como [u8; N * 2]. Eso da generic parameters may not be used in const operations (los parámetros genéricos no se pueden usar en operaciones constantes) y necesita una funcionalidad que solo existe en nightly. Si eres tú quien llama a empty_row(size) con un size de tiempo de ejecución, vuelve a la Solución 2. Mira Genéricos.

Un tamaño que viene de .len() o de lo que escribe el usuario. let n = names.len(); let done = [false; n]; da E0435, porque n solo se conoce en tiempo de ejecución. Solución: let done = vec![false; n]; (Solución 2).

Un static que lee un let. static LINE_WIDTH: usize = width; da el mismo titular, con help: consider using `let` instead of `static` . Un static se decide en tiempo de compilación, igual que un const. Solución: usa let, o convierte también width en un const.

Un const dentro de una función genérica. En fn bytes<T>() -> usize { const SIZE: usize = std::mem::size_of::<T>(); SIZE }, el const no puede usar T. Ese es otro código, E0401, y la solución sigue la misma idea: let size = std::mem::size_of::<T>();.

Comprueba lo aprendido

1. ¿Por qué let n = 3; let a = [0; n]; da E0435, aunque está claro que n vale 3?

2. El tamaño de tu lista viene de un archivo que lee el programa. ¿Cuál es la solución correcta?

3. ¿Qué línea compila?

Puntuación: 0 / 3