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.
El mensaje de error
Sección titulada «El mensaje de error»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);}Qué significa
Sección titulada «Qué significa»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.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»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.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»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íasTambié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 rondasEsta 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.
Solución 3: Usa let en lugar de const
Sección titulada «Solución 3: Usa let en lugar de const»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.0024.00El 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 casillasUn 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.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»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>();.
Errores relacionados
Sección titulada «Errores relacionados»- E0434: can’t capture dynamic environment in a fn item: la misma regla de “los elementos no ven las variables locales”, para una
fnescrita dentro de otra función. - E0401: can’t use generic parameters from outer item: una
fno unconstanidado que usa elTde la función externa. - E0015:
cannot call non-const function `args` in constants, cuando unconstllama a una función normal comostd::env::args()en lugar de leer una variable. Solución:let, o unaconst fn. - E0308: mismatched types: lo que obtienes después de la Solución 1 si un array con otra longitud va donde se espera un
[f64; DAYS]. - E0658: use of unstable feature: si recurres a funcionalidades constantes que solo existen en nightly para esquivar este error.
- Todos los errores del compilador: la lista completa de páginas de errores.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»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
Para leer más
Sección titulada «Para leer más»- E0435 — Rust error codes index (en inglés): la explicación oficial, con la solución del
const. - Constants — El libro de Rust (en inglés): en qué se diferencia
constdelet, y por qué las constantes necesitan un tipo. También existe una traducción al español de la comunidad del libro. - Constant evaluation — The Rust Reference (en inglés): qué expresiones exactamente puede calcular el compilador al compilar, y dónde son obligatorias (longitudes de arrays,
const,static). - Storing Lists of Values with Vectors — El libro de Rust (en inglés): la lista que puede crecer, para cuando el tamaño solo se conoce en tiempo de ejecución.
- Const generics MVP — Rust Blog (en inglés): cómo funciona
<const N: usize>, con ejemplos de arrays, y qué puede y qué no puede hacer Rust estable con ello.