ProgrammationIngénieur en programmation système / Développeur Rust Senior

Qu'est-ce que l'Unsafe Rust, pourquoi est-il nécessaire, quelles sont ses règles et comment utiliser correctement les blocs unsafe pour minimiser les risques ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

L'Unsafe Rust est une extension du sous-ensemble sûr de Rust, permettant d'effectuer des opérations que le compilateur ne peut pas vérifier en termes de possession, de durée de vie et d'aliasing. Les principales applications incluent : l'interaction avec des bibliothèques bas niveau, FFI, le contrôle manuel de la mémoire, la mise en œuvre d'abstractions ne s'inscrivant pas dans le modèle sûr de Rust.

Les caractéristiques clés de unsafe :

  • Pour l'utiliser, il est nécessaire de déclarer explicitement le bloc : unsafe { ... } ou la fonction : unsafe fn some_func()
  • Dans le bloc unsafe, des opérations dangereuses sont autorisées : déréférencement de raw pointer, appel de fonctions et méthodes dangereuses, accès à union, variables statiques mutables, mise en œuvre de méthodes de mémoire non structurée
  • L'utilisation de unsafe ne rend pas tout le code dans le bloc dangereux pour tout le langage — il s'engage seulement à garantir manuellement la correction.

Exemple :

let x: i32 = 10; let ptr: *const i32 = &x as *const i32; unsafe { println!("Valeur : {}", *ptr); // déréférencement de raw pointer }

Question avec un piège

Le code à l'intérieur d'un bloc unsafe est-il complètement dangereux et non vérifié par le compilateur, ou le compilateur continue-t-il à appliquer les règles du borrow checker et d'autres vérifications ?

Réponse : Non, à l'intérieur d'un bloc unsafe, le compilateur continue de vérifier de nombreuses règles de Rust (par exemple, typage, règles de possession, syntaxe), mais permet seulement d'exécuter des actions qui autrement ne seraient pas autorisées. On ne peut pas désactiver complètement le borrow checker !

Exemple :

let mut x = 0; let r1 = &mut x as *mut i32; // Interdit : let r2 = &mut x as *mut i32; // même à l'intérieur de unsafe, des erreurs surgiront si la mutabilité est violée

Exemples réels d'erreurs dues à une méconnaissance des subtilités du sujet


Histoire

Dans une bibliothèque de fichiers asynchrone, un raw pointer a été utilisé pour contrôler un tampon, mais on a oublié de suivre correctement la durée de vie du tampon. En conséquence, lors de la fermeture du fichier, le pointeur est devenu "dangling" et l'accès a conduit à un comportement indéfini (la section unsafe n'a pas sauvé de use-after-free).


Histoire

Dans une implémentation personnelle d'une structure similaire à Vec, le programmeur a élargi manuellement le tampon via unsafe. Une erreur de décalage a conduit à une copie incorrecte des éléments et à une corruption des données, car l'alignement et la disposition des types n'ont pas été pris en compte.


Histoire

Dans un descripteur de threads, un pointeur mutable statique (static mut) a été utilisé sans synchronisation appropriée. En raison de cela, en mode multithread, une data race est survenue accidentellement, entraînant un crash sporadique de l'application, détectable uniquement par fuzzing.