Les programmeurs ont depuis longtemps été confrontés à la question de la sécurité dans les environnements multithreads, rencontrant fréquemment des problèmes de concurrence, de données incohérentes et de fuites de mémoire. Rust a mis en œuvre une approche unique avec les marker-traits Send et Sync pour minimiser ces problèmes dès la phase de compilation.
Problème : l'absence de contrôle d'accès aux données partagées entre les threads, conduisant à des erreurs difficiles à détecter. Dans de nombreux langages, la responsabilité incombe entièrement au programmeur, tandis qu'en Rust, le compilateur vérifie ce qui peut être transféré/partagé entre les threads.
Solution : le trait Send garantit la possibilité de transférer un objet d'un thread à un autre en toute sécurité. Sync — la possibilité d'accéder simultanément à une référence d'objet à partir de différents threads. Presque tous les types standard en Rust implémentent automatiquement ces traits, tandis que les types personnalisés peuvent les implémenter manuellement ou les interdire via impl !Send ou impl !Sync pour des cas spécifiques.
Exemple de code :
use std::sync::{Arc, Mutex}; use std::thread; let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } // counter sera toujours égal à 10 sans course !
Caractéristiques clés :
Un type avec des pointeurs bruts peut-il être Send ou Sync ?
Non, si un type contient un pointeur brut ou des ressources sans garanties de sécurité multithread, il n'implémente pas ces traits, ou le développeur doit les implémenter manuellement en prenant l'entière responsabilité (souvent avec unsafe impl Send/Sync).
Rc<T> et RefCell<T> sont-ils Send ou Sync ?
Non, Rc<T> et RefCell<T> ne sont pas sûrs pour une utilisation multithread (ni Send, ni Sync). Pour les scénarios multithreads, Arc<T> et Mutex/RwLock sont utilisés.
Que se passe-t-il si une variable statique contient un type sans Sync implémenté ?
Rust ne permettra pas à une telle variable statique d'exister : elle doit être Sync, sinon le compilateur renverra une erreur.
Un jeune développeur place un objet Rc dans thread::spawn — le code ne compile que si Rc n'est pas passé entre les threads. En essayant de retirer Rc de thread::spawn, une erreur de compilation se produit, car Rc n'implémente pas Send et n'est pas protégé contre les courses.
Avantages :
Inconvénients :
Utilise Arc+Mutex pour un compteur multithread, tous les threads travaillent avec les mêmes données via une interface sécurisée pour les threads. Pas de courses, le code est sûr et robuste.
Avantages :
Inconvénients :