Deux traits qu'on n'écrit jamais décident ce qui a le droit de traverser un thread.Two traits you never write decide what is allowed to cross a thread.
Le numéro précédent a laissé une porte fermée. move donne une donnée à un seul thread ; thread::scope la prête à plusieurs, mais en lecture seule — parce que « aliasing XOR mutation » interdit que deux threads la tiennent pendant que l'un écrit. Reste le cas le plus courant en pratique : plusieurs threads qui veulent muter la même donnée. Un compteur, un cache, un état partagé. Avant d'y arriver, il faut nommer la mécanique invisible qui gouvernait déjà le refus du №1 : Send et Sync. Ce sont deux traits marqueurs, auto-dérivés par le compilateur, qui répondent à deux questions précises — cette donnée peut-elle partir dans un autre thread (Send) ? une référence vers elle peut-elle être partagée entre threads (Sync) ? Toute la sûreté de la concurrence en Rust repose sur ces deux réponses, calculées à la compilation, sans qu'on écrive une ligne.The previous issue left one door shut. move gives data to a single thread; thread::scope lends it to several, but read-only — because 'aliasing XOR mutation' forbids two threads holding it while one writes. That leaves the most common case in practice: several threads wanting to mutate the same data. A counter, a cache, a shared state. Before getting there, we must name the invisible machinery that already governed the №1 refusal: Send and Sync. These are two marker traits, auto-derived by the compiler, answering two precise questions — can this data leave for another thread (Send)? can a reference to it be shared between threads (Sync)? All of Rust's concurrency safety rests on these two answers, computed at compile time, without writing a line.
// la signature de thread::spawn impose DEUX bornes — pas une pub fn spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T + Send + 'static, // 'static (vu au №1) ET Send (ce numéro) T: Send + 'static; // 'static → « ne dépend d'aucun emprunt à durée limitée » (le QUAND) // Send → « la possession peut PARTIR vers un autre thread » (le QUOI) // le refus du №1 portait sur la durée de vie. voici l'autre moitié : ce qui a // le DROIT de traverser. un Rc, par exemple, ne l'a pas — et le compilateur le sait.
Au №1, on a buté sur 'static : quand la donnée peut vivre. Mais spawn exige une seconde garantie — Send : quoi peut quitter le thread courant. Les deux sont des traits marqueurs : ils ne portent aucune méthode, ils étiquettent simplement un type comme sûr pour une opération. Et ils sont auto-dérivés : un type est Send (resp. Sync) si tous ses champs le sont. Tu n'écris presque jamais impl Send — le compilateur le déduit, et c'est ce calcul silencieux qui rend le data race inexprimable.In №1, we hit 'static: when the data may live. But spawn demands a second guarantee — Send: what may leave the current thread. Both are marker traits: they carry no method, they simply tag a type as safe for an operation. And they're auto-derived: a type is Send (resp. Sync) if all its fields are. You almost never write impl Send — the compiler deduces it, and that silent computation is what makes a data race inexpressible.
« Pour partager et muter entre threads, il faut deux outils, pas un : qui partage la possession, qui rétablit l'exclusivité ? » Arc répond au premier, Mutex au second — et ils vont ensemble parce que ni l'un ni l'autre ne suffit. Arc seul partage en lecture ; Mutex seul ne se partage pas. La concurrence sûre, c'est l'assemblage exact des deux.'To share and mutate across threads, you need two tools, not one: which shares ownership, which restores exclusivity?' Arc answers the first, Mutex the second — and they go together because neither alone suffices. Arc alone shares read-only; Mutex alone can't be shared. Safe concurrency is the exact assembly of both.
Send : la possession d'une valeur peut être transférée vers un autre thread (la valeur « part »). Sync : une référence partagée &T peut être utilisée depuis plusieurs threads à la fois — ce qui revient exactement à dire « &T est Send ». Presque tout est Send et Sync. Les exceptions sont instructives. Rc (compteur non atomique) n'est ni Send ni Sync. Cell et RefCell (mutabilité intérieure non synchronisée) sont Send — on peut les donner à un autre thread — mais pas Sync : on ne peut pas les partager. Les pointeurs bruts ne sont ni l'un ni l'autre. Chaque exception bloque exactement l'opération qui provoquerait une course.Send: ownership of a value can be transferred to another thread (the value 'leaves'). Sync: a shared reference &T can be used from several threads at once — which is exactly to say '&T is Send'. Almost everything is Send and Sync. The exceptions are instructive. Rc (non-atomic counter) is neither Send nor Sync. Cell and RefCell (unsynchronized interior mutability) are Send — you can hand them to another thread — but not Sync: you can't share them. Raw pointers are neither. Each exception blocks exactly the operation that would cause a race.