Rust-Fehler E0435: attempt to use a non-constant value in a constant (let statt const)
E0435 bedeutet: Etwas, das schon beim Kompilieren feststehen muss, versucht einen Wert zu benutzen, den es erst gibt, wenn dein Programm läuft. Meistens passiert das bei einer Array-Länge wie [0.0; days] oder bei einem const, der eine let-Variable liest. Der Code sieht völlig vernünftig aus, und in vielen Sprachen würde er funktionieren. In Rust rechnet aber der Compiler Konstanten und Array-Längen aus, bevor das Programm überhaupt startet. Deshalb können sie keine normalen Variablen ansehen. Der Fehler ist sehr häufig, und die Lösung ist meistens ein einziges Wort. (Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.)
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“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; |Auf Deutsch: „Versuch, einen nicht konstanten Wert in einer Konstanten zu benutzen.“ Der Kommentar im Code heißt: „Die Länge eines Arrays muss beim Kompilieren bekannt sein.“
Dieses Programm erzeugt den Fehler:
fn main() { let days = 7; let rainfall = [0.0; days]; // ❌ an array's length must be known at compile time println!("{:?}", rainfall);}Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Die ^^^^ zeigen in Zeile 3 auf days, mitten in [0.0; days]. Das ist die „Wiederholungs“-Form eines Arrays: sieben Kopien von 0.0. Der Teil nach dem ; ist die Länge des Arrays, und die Länge gehört zum Typ des Arrays: [f64; 7] und [f64; 8] sind zwei verschiedene Typen. Deshalb muss rustc die Zahl schon beim Kompilieren kennen.
Aber days ist eine let-Variable. Für rustc ist ein let ein Wert, den es nur gibt, während das Programm läuft – auch wenn daneben einfach eine 7 steht. Genau das meint „non-constant value“ (nicht konstanter Wert).
Die Zeile help: zeigt die Lösung: Mach aus Zeile 2 einen const. Achte auf den Platzhalter /* Type */: Ein const braucht immer einen ausgeschriebenen Typ, also schreibst du const DAYS: usize = 7;.
Dieselbe Überschrift erscheint, wenn ein const oder static eine let-Variable liest, etwa const PRICE_WITH_TAX: f64 = base_price * 1.2;. Dann schlägt help: die umgekehrte Richtung vor: let statt const.
Das ist wie beim Drucken eines Formulars. Wie viele Kästchen das Formular hat, entscheidet die Druckerei, bevor es irgendjemand ausfüllt. Die Zahl der Kästchen kann nicht von einer Antwort abhängen, die später jemand ins Formular schreibt.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Rust kennt zwei Zeitpunkte: die Compile-Zeit, wenn cargo build deinen Code in ein Programm verwandelt, und die Laufzeit, wenn das Programm läuft. Ein const, ein static und die Länge eines Arrays werden alle zur Compile-Zeit festgelegt. Der Compiler schreibt sie direkt ins Programm. Deshalb sind sie schnell und überall benutzbar.
Eine let-Variable gehört zur Laufzeit. Ihr Wert kann aus einer Datei kommen, aus einer Benutzereingabe oder aus einer Rechnung, die erst beim Ausführen passiert. Rust macht für let days = 7; keine Ausnahme, nur weil hier zufällig eine feste Zahl steht. So bleibt die Regel einfach, und dein Code geht nicht an dem Tag kaputt, an dem du 7 durch read_days_from_config() ersetzt.
Die entscheidende Frage ist also: Kenne ich diese Zahl schon, wenn ich den Code schreibe? Wenn ja, mach einen const daraus. Wenn nein, kann sie keine Array-Länge sein, und ein Vec ist das richtige Werkzeug. Siehe Konstanten und Arrays oder Vektoren?.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Die Größe zu einem const machen
Abschnitt betitelt „Lösung 1: Die Größe zu einem const machen“Nimm diese Lösung, wenn die Zahl in deinem Code feststeht, wie die 7 Tage einer Woche. Mach, was help: sagt, mit zwei kleinen Änderungen: Schreib den Typ dazu (usize für eine Länge) und nimm einen Namen in GROSSBUCHSTABEN, so schreibt man Konstanten in Rust:
const DAYS: usize = 7; // ✅ beim Kompilieren bekannt
fn main() { let mut rainfall = [0.0; DAYS]; rainfall[2] = 4.5; rainfall[5] = 1.0; println!("{:?}", rainfall); println!("{} Tage", rainfall.len());}Ausgabe:
[0.0, 0.0, 4.5, 0.0, 0.0, 1.0, 0.0]7 TageDu kannst den const auch in main schreiben, genau dort, wo vorher das let stand. Ganz oben in der Datei können ihn alle Funktionen benutzen. Behältst du den kleingeschriebenen Namen days, kompiliert es trotzdem, aber mit einer Warnung, dass Konstanten großgeschrieben werden sollen.
Lösung 2: Einen Vec nehmen, wenn die Größe erst zur Laufzeit bekannt ist
Abschnitt betitelt „Lösung 2: Einen Vec nehmen, wenn die Größe erst zur Laufzeit bekannt ist“Nimm diese Lösung, wenn die Zahl aus dem laufenden Programm kommt: die Länge einer Liste, ein Wert aus einer Datei, eine Benutzereingabe, ein Funktionsparameter. Arrays haben eine feste Größe; ein Vec ist die wachsende Liste, die jede Größe haben kann. vec![value; n] ist dieselbe „Wiederholungs“-Form, und hier darf n jede beliebige Variable sein:
fn main() { let players = ["Ana", "Ben", "Chen"]; let rounds = players.len() * 2;
let mut scores = vec![0; rounds]; // ✅ die Länge eines Vec darf aus einer Variable kommen scores[0] = 10; scores[3] = 7; println!("{:?}", scores); println!("{} Runden", scores.len());}Ausgabe:
[10, 0, 0, 7, 0, 0]6 RundenDas ist viel öfter die richtige Lösung, als man denkt. Ein Vec funktioniert fast überall, wo ein Array funktioniert: Indexzugriff, .len(), Schleifen, Slices (&scores[1..3]). Der Geschwindigkeitsunterschied ist bei normalen Programmen winzig. Siehe Vektoren.
Lösung 3: let statt const nehmen
Abschnitt betitelt „Lösung 3: let statt const nehmen“Nimm diese Lösung, wenn ein const (oder static) eine let-Variable liest. Diese Variante des Fehlers sieht so aus:
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`Das fehlerhafte Programm (der Kommentar heißt: „Ein const kann keine let-Variable lesen“):
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);}Wird der Wert erst berechnet, während das Programm läuft, ist er eine normale Variable. Mach also ein let daraus (mit kleingeschriebenem Namen). Stehen beide Werte wirklich fest, geht es auch andersherum: Mach den ersten Wert ebenfalls zu einem const. Hier siehst du beides:
const BASE_PRICE: f64 = 20.0;const PRICE_WITH_TAX: f64 = BASE_PRICE * 1.2; // ✅ ein const darf einen anderen const benutzen
fn main() { let base_price = 35.0; let price_with_tax = base_price * 1.2; // ✅ ein let darf alles benutzen println!("{:.2}", price_with_tax); println!("{:.2}", PRICE_WITH_TAX);}Ausgabe:
42.0024.00Die Zeile help: schlägt let PRICE_WITH_TAX vor. Das kompiliert, aber du bekommst eine Warnung, dass Variablen in snake_case geschrieben werden sollen. Benenn sie in price_with_tax um.
Lösung 4: Const Generics oder eine const fn, wenn du wirklich ein Array brauchst
Abschnitt betitelt „Lösung 4: Const Generics oder eine const fn, wenn du wirklich ein Array brauchst“Nimm diese Lösung, wenn du ein echtes Array mit fester Größe willst, die Größe aber von außerhalb einer Funktion kommen oder berechnet werden soll. Auch ein Funktionsparameter ist ein Laufzeitwert, also ergibt das hier E0435 (rustc sagt, der Parameter would need to be a const, müsste also ein const sein):
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 | ^^^^Das fehlerhafte Programm (der Kommentar heißt: „size ist erst bekannt, wenn die Funktion läuft“):
fn empty_row(size: usize) { let row = [0u8; size]; // ❌ `size` is only known when the function runs println!("{:?}", row);}
fn main() { empty_row(3);}Ein Const Generic macht genau das, was rustc verlangt: <const N: usize> macht die Größe zu einem Parameter, der zur Compile-Zeit feststeht. Du schreibst empty_row::<5>(), oder Rust leitet ihn aus dem Typ ab. Eine const fn ist eine Funktion, die der Compiler schon beim Kompilieren ausführen kann. Damit kannst du eine Konstante berechnen:
fn empty_row<const N: usize>() -> [u8; N] { [0; N] // ✅ N ist ein Parameter, der beim Kompilieren feststeht}
const fn cells(width: usize, height: usize) -> usize { width * height}
const WIDTH: usize = 4;const HEIGHT: usize = 3;const CELLS: usize = cells(WIDTH, HEIGHT); // ✅ vom Compiler berechnet
fn main() { let row: [u8; 3] = empty_row(); let wide = empty_row::<5>(); println!("{:?}", row); println!("{:?}", wide);
let board = [0u8; CELLS]; println!("{} Zellen", board.len());}Ausgabe:
[0, 0, 0][0, 0, 0, 0, 0]12 ZellenEine Grenze gibt es auf Stable-Rust: In einer generischen Funktion darfst du N allein als Länge benutzen, aber keine Rechnung damit wie [u8; N * 2]. Das ergibt generic parameters may not be used in const operations und braucht ein Feature, das es nur auf Nightly gibt. Rufst du selbst empty_row(size) mit einem size auf, das erst zur Laufzeit feststeht, geh zurück zu Lösung 2. Siehe Generics.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Eine Größe aus .len() oder einer Benutzereingabe. let n = names.len(); let done = [false; n]; ergibt E0435, weil n erst zur Laufzeit bekannt ist. Lösung: let done = vec![false; n]; (Lösung 2).
Ein static, der ein let liest. static LINE_WIDTH: usize = width; ergibt dieselbe Überschrift, mit help: consider using `let` instead of `static` . Ein static wird genau wie ein const zur Compile-Zeit festgelegt. Lösung: Nimm let, oder mach width ebenfalls zu einem const.
Ein const in einer generischen Funktion. In fn bytes<T>() -> usize { const SIZE: usize = std::mem::size_of::<T>(); SIZE } darf der const kein T benutzen. Das ist ein anderer Code, E0401, und die Lösung folgt derselben Idee: let size = std::mem::size_of::<T>();.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0434: can’t capture dynamic environment in a fn item; dieselbe Regel „Items sehen keine lokalen Variablen“, für ein
fn, das in einer anderen Funktion steht. - E0401: can’t use generic parameters from outer item; ein inneres
fnoder einconst, das dasTder äußeren Funktion benutzt. - E0015:
cannot call non-const function `args` in constants, wenn einconsteine normale Funktion wiestd::env::args()aufruft, statt eine Variable zu lesen. Lösung:letoder eineconst fn. - E0308: mismatched types; das bekommst du nach Lösung 1, wenn ein Array mit falscher Länge dort landet, wo
[f64; DAYS]erwartet wird. - E0658: use of unstable feature, wenn du zu Nightly-Features für Konstanten greifst, um den Fehler zu umgehen.
- Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Warum ergibt let n = 3; let a = [0; n]; den Fehler E0435, obwohl n doch eindeutig 3 ist?
2. Die Größe deiner Liste kommt aus einer Datei, die das Programm liest. Was ist die richtige Lösung?
3. Welche Zeile kompiliert?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0435 – Rust error codes index (auf Englisch): die offizielle Erklärung, mit der Lösung über
const. - Konstanten deklarieren – Das Rust-Buch: wie sich
constvonletunterscheidet und warum Konstanten einen Typ brauchen. - Constant evaluation – The Rust Reference (auf Englisch): welche Ausdrücke der Compiler genau zur Compile-Zeit ausrechnen kann und wo sie Pflicht sind (Array-Längen,
const,static). - Wertlisten in Vektoren ablegen – Das Rust-Buch: die wachsende Liste für Größen, die erst zur Laufzeit bekannt sind.
- Const generics MVP – Rust Blog (auf Englisch): wie
<const N: usize>funktioniert, mit Array-Beispielen, und was Stable-Rust damit kann und was nicht.