In Rust worden globale variabelen gedeclareerd met het sleutelwoord static. Dergelijke variabelen leven gedurende de hele uitvoering van het programma. Standaard is de toegang tot onveranderlijke static veilig, maar voor veranderlijke is het nodig om unsafe te gebruiken, omdat de compiler niet kan garanderen dat er geen race conditions zijn.
Voor veilige toegang tot veranderlijke globale variabelen worden speciale wrapper-types gebruikt: Mutex, RwLock, Atomic* types of externe crates (lazy_static, once_cell). Initialisatie is "luiaardig" als het uitstellen van het creëren van een object bij de eerste toegang vereist is.
Voorbeelden:
static COUNTER: AtomicUsize = AtomicUsize::new(0); static ref CONFIG: Config = read_config(); // met lazy_static of once_cell
Kan je een veranderlijke globale variabele zonder speciale types declareren, als deze alleen uit één thread gebruikt gaat worden? Moet je unsafe schrijven?
Antwoord:
De compiler zal vereisen om unsafe te gebruiken bij elke wijziging van een globale variabele, zelfs als de logica single-threaded is. Dit is een algemene vereiste voor alle static mut. Alleen als de variabele is verpakt in een synchroniserende structuur (Mutex, atomisch, etc.), zal de compiler veilige toegang toestaan.
static mut VALUE: i32 = 0; unsafe { VALUE += 1; }
Verhaal
In een webservice werd bij de initialisatie van de globale cache een gewone static-variabele met een string gebruikt. Meerdere threads schreven er gelijktijdig naar, er ontstonden "bitten" in het geheugen en de applicatie viel om door race conditions.
Verhaal
In een command-line tool in Rust werd een statische variabele zonder synchronisatie gewijzigd, omdat het "alleen via main werkt". Later werd de code aangepast voor multithreading, waarbij global mutable state werd vergeten, wat leidde tot moeilijk te traceren bugs.
Verhaal
In een embedded-programma werd de static CONFIG verkeerd geïnitialiseerd via een functie bij opstart, maar werd niet gegarandeerd dat deze precies één keer zou worden aangeroepen. Als gevolg daarvan kreeg een deel van de code toegang tot de niet-geïnitialiseerde configuratie (null reference).