In Rust werden konstante Ausdrücke (const) zur Kompilierzeit berechnet, sodass Werte erstellt werden, die Teil des Programms werden, bevor es ausgeführt wird. Solche Ausdrücke werden verwendet, um die Größen von Arrays, Werte in statischen Strukturen, generische Parameter und andere Situationen zu definieren, in denen unveränderliche Konstanten mit fest bekannten Werten benötigt werden.
In Rust können "konstante" Funktionen (const fn) erstellt werden, die innerhalb anderer const-Ausdrücke oder zur Initialisierung konstanter Variablen verwendet werden können. Der Compiler garantiert, dass solche Ausdrücke keine ungültigen Operationen enthalten (z. B. auf den Speicher zuzugreifen).
Beispiel:
const fn fib(n: u32) -> u32 { match n { 0 | 1 => 1, _ => fib(n - 1) + fib(n - 2), } } const F8: u32 = fib(8); const ARR: [u32; F8 as usize] = [0; F8 as usize]; // Array der Größe 34
In diesem Beispiel werden der Wert F8 und die Größe des Arrays ARR zur Kompilierzeit berechnet.
Was unterscheidet eine const Funktion von einer normalen Funktion, und kann man jede Funktion als const fn deklarieren?
Antwort:
Nein, nicht jede Funktion kann als const fn deklariert werden. const fn kann nur erlaubte Operationen enthalten, die keine Nebenwirkungen oder Arbeit mit unsicherem Speicher zulassen. Zum Beispiel kann man in const fn keine Datei öffnen oder Speicher dynamisch allozieren.
const fn add(x: i32, y: i32) -> i32 { x + y // erlaubt } // und das wird nicht kompiliert: const fn fail() -> String { // Fehler! String::from("err") }
Geschichte
In einem Projekt wurde versucht, den Hashwert einer Zeichenkette zur Kompilierzeit über eine konstante Funktion zu berechnen, dabei wurden jedoch in dieser Funktion Standardmethoden aus
HashMapund Speicherallokationen verwendet. Das Programm konnte nicht kompiliert werden und gab unverständliche Fehler über die Unzulässigkeit von Operationen in const fn aus.
Geschichte
In einer großen Embedded-Entwicklung definierte ein Entwickler eine konstante Struktur mit Feldern, die eine Berechnung zur Kompilierzeit erforderten, verwendete jedoch innerhalb der Initialisierung Funktionen aus einem externen Crate, die nicht als
const fngekennzeichnet waren. Dies führte dazu, dass diese Logik nicht zur Bestimmung der Größen statischer Puffer verwendet werden konnte.
Geschichte
Im Code wurde der Unterschied zwischen
staticundconstverwechselt, als versucht wurde, eine "Konstante" zur Laufzeit zu ändern, was zu implizitem UB (Undefined Behavior) führte, da Konstanten in Rust nicht im Speicher wie Werte platziert werden, sondern vom Compiler an den Stellen verwendet werden, an denen sie verwendet werden, was eine Änderung ihrer Werte überhaupt nicht zulässt.