Errore Rust E0435: attempt to use a non-constant value in a constant (valore non costante)
E0435 significa: qualcosa che deve essere noto mentre il programma viene compilato cerca di usare un valore che esiste solo mentre il programma gira. Il posto tipico è la lunghezza di un array, come [0.0; days], oppure una const che legge una variabile let. Il codice sembra del tutto ragionevole, e in molti linguaggi funzionerebbe. In Rust le costanti e le lunghezze degli array le calcola il compilatore, prima ancora che il programma parta, quindi non possono guardare le variabili normali. È un errore molto comune, e di solito la correzione è una parola. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”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; |Il compilatore dice: stai cercando di usare un valore non costante dentro una costante, e ti consiglia const al posto di let. Questo è il programma che produce l’errore:
fn main() { let days = 7; let rainfall = [0.0; days]; // ❌ an array's length must be known at compile time println!("{:?}", rainfall);}Cosa significa
Sezione intitolata “Cosa significa”Il ^^^^ indica days alla riga 3, dentro [0.0; days]. È la forma «a ripetizione» di un array: sette copie di 0.0. La parte dopo il ; è la lunghezza dell’array, e la lunghezza fa parte del tipo dell’array: [f64; 7] e [f64; 8] sono due tipi diversi. Quindi rustc deve conoscere il numero mentre compila.
Ma days è una variabile let. Per rustc un let è un valore che esiste solo mentre il programma gira, anche quando accanto hai scritto un semplice 7. «Non-constant value» (valore non costante) significa proprio questo.
L’help: mostra la correzione: trasformare la riga 2 in una const. Nota il segnaposto /* Type */: una const ha sempre bisogno del tipo scritto per esteso, quindi scriverai const DAYS: usize = 7;.
La stessa prima riga compare quando una const o una static legge una variabile let, come const PRICE_WITH_TAX: f64 = base_price * 1.2;. In quel caso l’help: suggerisce la direzione opposta: usare let al posto di const.
È come stampare un modulo. Il numero di caselle sul modulo si decide in tipografia, prima che qualcuno lo compili. Non puoi far dipendere il numero di caselle da una risposta che qualcuno scriverà sul modulo più tardi.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Rust ha due momenti: la fase di compilazione, quando cargo build trasforma il tuo codice in un programma, e l’esecuzione, quando il programma gira. Una const, una static e la lunghezza di un array si decidono tutte in fase di compilazione. Il compilatore le scrive direttamente nel programma. Ecco perché sono veloci e perché si possono usare ovunque.
Una variabile let appartiene all’esecuzione. Il suo valore può arrivare da un file, dall’input dell’utente o da un calcolo che avviene solo quando il codice gira. Rust non fa eccezioni per let days = 7; solo perché in questo caso è un letterale: la regola resta semplice, e il tuo codice non si rompe il giorno in cui cambi 7 in read_days_from_config().
Quindi la domanda da farti è: conosco questo numero quando scrivo il codice? Se sì, rendilo una const. Se no, non può essere la lunghezza di un array, e lo strumento giusto è un Vec. Vedi Variabili in Rust e Array in Rust.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: rendi la dimensione una const
Sezione intitolata “Soluzione 1: rendi la dimensione una const”Scegli questa quando il numero è fisso nel tuo codice, come i 7 giorni di una settimana. Fai quello che dice l’help:, con due piccole modifiche: scrivi il tipo (usize per una lunghezza) e usa un nome in MAIUSCOLO, che è lo stile di Rust per le costanti:
const DAYS: usize = 7; // ✅ noto in fase di compilazione
fn main() { let mut rainfall = [0.0; DAYS]; rainfall[2] = 4.5; rainfall[5] = 1.0; println!("{:?}", rainfall); println!("{} giorni", rainfall.len());}Output:
[0.0, 0.0, 4.5, 0.0, 0.0, 1.0, 0.0]7 giorniPuoi mettere la const anche dentro main, proprio dove c’era il let. In cima al file, ogni funzione può usarla. Se tieni il nome minuscolo days, compila lo stesso, ma con un warning che ti ricorda che le costanti vanno in maiuscolo.
Soluzione 2: usa un Vec quando la dimensione si conosce solo durante l’esecuzione
Sezione intitolata “Soluzione 2: usa un Vec quando la dimensione si conosce solo durante l’esecuzione”Scegli questa quando il numero arriva dal programma in esecuzione: la lunghezza di una lista, un valore da un file, l’input dell’utente, un parametro di funzione. Gli array hanno una dimensione fissa; un Vec è la lista che può crescere e avere qualsiasi dimensione. vec![valore; n] è la stessa forma «a ripetizione», e qui n può essere qualsiasi variabile:
fn main() { let players = ["Ana", "Ben", "Chen"]; let rounds = players.len() * 2;
let mut scores = vec![0; rounds]; // ✅ la lunghezza di un Vec può venire da una variabile scores[0] = 10; scores[3] = 7; println!("{:?}", scores); println!("{} turni", scores.len());}Output:
[10, 0, 0, 7, 0, 0]6 turniQuesta è la correzione giusta molto più spesso di quanto si pensi. Un Vec funziona quasi ovunque funzioni un array: indici, .len(), cicli, slice (&scores[1..3]). Per i programmi normali la differenza di velocità è minima. Vedi Vettori in Rust.
Soluzione 3: usa let al posto di const
Sezione intitolata “Soluzione 3: usa let al posto di const”Scegli questa quando una const (o una static) legge una variabile let. Questa versione dell’errore ha questo aspetto:
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`Il programma che non compila:
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);}Se il valore si calcola mentre il programma gira, è una variabile normale, quindi rendila un let (con un nome minuscolo). Se entrambi i valori sono davvero fissi, funziona anche l’altra strada: rendi const anche il primo. Qui sotto ci sono tutte e due:
const BASE_PRICE: f64 = 20.0;const PRICE_WITH_TAX: f64 = BASE_PRICE * 1.2; // ✅ una const può usare un'altra const
fn main() { let base_price = 35.0; let price_with_tax = base_price * 1.2; // ✅ un let può usare qualsiasi cosa println!("{:.2}", price_with_tax); println!("{:.2}", PRICE_WITH_TAX);}Output:
42.0024.00La riga help: suggerisce let PRICE_WITH_TAX; compila, ma ricevi un warning che ti ricorda che le variabili vanno in snake_case. Rinominala in price_with_tax.
Soluzione 4: const generics o una const fn, quando ti serve davvero un array
Sezione intitolata “Soluzione 4: const generics o una const fn, quando ti serve davvero un array”Scegli questa quando vuoi un vero array a dimensione fissa, ma la dimensione deve arrivare da fuori una funzione, o va calcolata. Anche un parametro di funzione è un valore dell’esecuzione, quindi questo dà E0435 (rustc dice che il parametro would need to be a const, dovrebbe essere una 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 | ^^^^Il programma che non compila:
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 fa quello che chiede rustc: <const N: usize> rende la dimensione un parametro della fase di compilazione, scritto empty_row::<5>() oppure dedotto dal tipo. Una const fn è una funzione che il compilatore può eseguire in fase di compilazione, quindi puoi usarla per calcolare una costante:
fn empty_row<const N: usize>() -> [u8; N] { [0; N] // ✅ N è un parametro della fase di compilazione}
const fn cells(width: usize, height: usize) -> usize { width * height}
const WIDTH: usize = 4;const HEIGHT: usize = 3;const CELLS: usize = cells(WIDTH, HEIGHT); // ✅ calcolato dal compilatore
fn main() { let row: [u8; 3] = empty_row(); let wide = empty_row::<5>(); println!("{:?}", row); println!("{:?}", wide);
let board = [0u8; CELLS]; println!("{} caselle", board.len());}Output:
[0, 0, 0][0, 0, 0, 0, 0]12 caselleUn limite su Rust stabile: dentro una funzione generica puoi usare N da solo come lunghezza, ma non un calcolo che lo contiene, come [u8; N * 2]. Questo dà generic parameters may not be used in const operations e richiede una funzionalità solo nightly. Se sei tu a chiamare empty_row(size) con una size dell’esecuzione, torna alla soluzione 2. Vedi Generici in Rust.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Una dimensione presa da .len() o dall’input dell’utente. let n = names.len(); let done = [false; n]; dà E0435, perché n si conosce solo durante l’esecuzione. Correzione: let done = vec![false; n]; (soluzione 2).
Una static che legge un let. static LINE_WIDTH: usize = width; dà la stessa prima riga, con help: consider using `let` instead of `static` . Anche una static si decide in fase di compilazione, proprio come una const. Correzione: usa let, oppure rendi const anche width.
Una const dentro una funzione generica. In fn bytes<T>() -> usize { const SIZE: usize = std::mem::size_of::<T>(); SIZE }, la const non può usare T. È un codice diverso, E0401, e la correzione segue la stessa idea: let size = std::mem::size_of::<T>();.
Errori correlati
Sezione intitolata “Errori correlati”- E0434: can’t capture dynamic environment in a fn item: la stessa regola «gli item non vedono le variabili locali», per una
fnscritta dentro un’altra funzione. - E0401: can’t use generic parameters from outer item: una
fno unaconstannidata che usa ilTdella funzione esterna. - E0015:
cannot call non-const function `args` in constants, quando unaconstchiama una funzione normale comestd::env::args()invece di leggere una variabile. Correzione:let, oppure unaconst fn. - E0308: mismatched types: quello che ottieni dopo la soluzione 1 se un array della lunghezza sbagliata finisce dove ci si aspetta
[f64; DAYS]. - E0658: use of unstable feature: se ricorri a funzionalità const solo nightly per aggirare il problema.
- Tutti gli errori del compilatore spiegati
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Perché let n = 3; let a = [0; n]; dà E0435, anche se n vale chiaramente 3?
2. La dimensione della tua lista arriva da un file che il programma legge. Qual è la correzione giusta?
3. Quale riga compila?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0435 — Rust error codes index (in inglese): la spiegazione ufficiale, con la correzione tramite
const. - Constants — The Rust Book (in inglese): in che cosa
constè diversa dalet, e perché le costanti hanno bisogno di un tipo. - Constant evaluation — The Rust Reference (in inglese): esattamente quali espressioni il compilatore sa calcolare in fase di compilazione, e dove sono obbligatorie (lunghezze degli array,
const,static). - Storing Lists of Values with Vectors — The Rust Book (in inglese): la lista che può crescere, da usare quando la dimensione si conosce solo durante l’esecuzione.
- Const generics MVP — Rust Blog (in inglese): come funziona
<const N: usize>, con esempi sugli array, e che cosa Rust stabile può e non può farci.