ProgrammatieSysteemsoftware-ingenieur / Rust Senior Developer

Wat is Unsafe Rust, waarom is het nodig, wat zijn de regels en hoe gebruik je unsafe-blokken op de juiste manier om risico's te minimaliseren?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

Unsafe Rust is een uitbreiding van de veilige subset van Rust die het mogelijk maakt om operaties uit te voeren die de compiler niet kan controleren op correctheid van eigendom, levensduur en aliasing. De belangrijkste toepassingsgebieden zijn: interactie met low-level bibliotheken, FFI, handmatige geheugentoewijzing, implementatie van abstracties die niet binnen het veilige model van Rust passen.

Belangrijkste kenmerken van unsafe:

  • Voor gebruik moet je het blok expliciet verklaren: unsafe { ... } of de functie: unsafe fn some_func()
  • In een unsafe-blok zijn onveilige operaties toegestaan: dereferencen van raw pointers, aanroepen van onveilige functies en methoden, toegang tot unions, statische mutable variabelen, implementatie van methoden voor ongestructureerd geheugen
  • Het gebruik van unsafe maakt de hele code in het blok niet gevaarlijk voor de hele taal — je dient handmatig de correctheid te waarborgen.

Voorbeeld:

let x: i32 = 10; let ptr: *const i32 = &x as *const i32; unsafe { println!("Value: {}", *ptr); // dereferencen van raw pointer }

Vraag met een valkuil

Is de code binnen een unsafe-blok volledig onveilig en niet gecontroleerd door de compiler, of past de compiler nog steeds de regels van de borrow checker en andere controles toe?

Antwoord: Nee, binnen een unsafe-blok blijft de compiler een aantal regels van Rust controleren (bijvoorbeeld typechecking, eigendomregels, syntaxis), maar staat alleen die acties toe die anders niet zouden zijn toegestaan. Je kunt de borrow checker niet volledig uitschakelen!

Voorbeeld:

let mut x = 0; let r1 = &mut x as *mut i32; // Verboden: let r2 = &mut x as *mut i32; // zelfs binnen unsafe zullen er fouten optreden als de mutabiliteit wordt geschonden

Voorbeelden van echte fouten door gebrek aan kennis over de nuances van het onderwerp


Geschiedenis

In een asynchrone bestandsbibliotheek werd een raw pointer gebruikt voor het controleren van een buffer, maar men vergat de levensduur van de buffer correct te volgen. Hierdoor, bij het sluiten van het bestand, werd de pointer "hangend" en toegang leidde tot undefined behavior (de unsafe-sectie redde niet van use-after-free).


Geschiedenis

In een eigen implementatie van een Vec-achtige structuur breidde de programmeur handmatig de buffer uit via unsafe. Een offsetfout leidde tot onjuiste kopieën van elementen en datacorruptie, omdat alignment en layout van types niet in aanmerking waren genomen.


Geschiedenis

In een thread descriptor werd een statische mutable pointer (static mut) gebruikt zonder de juiste synchronisatie. Hierdoor ontstond er per ongeluk een data race in multi-threaded modus, wat leidde tot sporadische crashes van de applicatie, alleen opgevangen door fuzzing.