ProgrammatieSysteem Rust ontwikkelaar

Hoe is constante evaluatie (const evaluation) in Rust ingericht? Wat zijn de verschillen tussen const, static en let, in welke gevallen kan (en moet) const fn worden gebruikt, en wat zijn de beperkingen bij het schrijven van berekeningen in compileertijd?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

Constante evaluatie in Rust staat toe om een deel van de berekeningen of initialisatie tijdens de compileertijd uit te voeren, in plaats van tijdens de uitvoering van het programma.

  • const declareert een onveranderlijke constante, berekend tijdens de compileertijd, en heeft geen adres in het geheugen:
const PI: f64 = 3.1415;
  • static declareert een globale variabele, gelegen in een bepaald geheugensegment (meestal .data of .bss), mutabiliteit is mogelijk (vereist unsafe):
static mut GLOBAL_COUNTER: i32 = 0;
  • let wordt gebruikt voor variabelen op de stack, hun waarde kan op runtime worden berekend, en ze moeten geïnitieerd worden voor het eerste gebruik.

const fn is een functie waarvan het resultaat kan worden gebruikt om de waarde van const of static te definiëren. Dergelijke functies kunnen worden aangeroepen in een constante context.

const fn factorial(n: usize) -> usize { if n == 0 { 1 } else { n * factorial(n - 1) } } const FACT_5: usize = factorial(5); // Compileert!

Beperkingen van const fn:

  • Je kunt alleen andere const fn's gebruiken,
  • Heap-allocatie is niet toegestaan (Box::new en dergelijke),
  • Onveilige operaties kunnen niet worden uitgevoerd,
  • Geen toegang tot externe variabelen en functies (tenzij ze const fn zijn).

Vragen met een valstrik.

Vraag: Mag je een willekeurige functie aanroepen in een const-context, als het resultaat niet verandert? Bijvoorbeeld zo:

fn add(a: i32, b: i32) -> i32 { a + b } const RES: i32 = add(1, 2);

Typisch onjuist antwoord: Ja, want de functie is zuiver en het resultaat is van tevoren bekend.

Juiste antwoord: Nee, de functie moet expliciet als const fn worden gedeclareerd, alleen dan kan deze worden aangeroepen binnen const-initialisatie. Gewone functies worden alleen tijdens de uitvoering aangeroepen!

Voorbeeld:

const fn add(a: i32, b: i32) -> i32 { a + b } const RES: i32 = add(1, 2); // Compileert!

Voorbeelden van echte fouten door onwetendheid over de nuances van het onderwerp.


Geschiedenis

In een project met berekeningen van driedimensionale geometrie probeerde een ontwikkelaar een waardetabel te declareren via het resultaat van een aanroep van een gewone functie, en niet const fn. Hierdoor ontstonden compilatiefouten en ging het voordeel van compile-time berekening verloren.


Geschiedenis

Het gebruik van static mut voor een globale cache leidde tot een datarace bij toegang vanuit meerdere threads (static mut is niet veilig!). Er had gebruikgemaakt moeten worden van Atomic of Mutex om de toegang tot de globale hulpbron te synchroniseren.


Geschiedenis

In een poging om de initialisatie van grote arrays te versnellen, werden ze als static gedefinieerd, maar vergeten dat static altijd een vast adres heeft, waardoor gegevens niet als lokaal in de processor cache kwamen en de operaties vertraagd werden op het hete pad van de serverlogica. Er had gebruikgemaakt moeten worden van lokale let-expressies met berekeningen in runtime.