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), Cell et RefCell (mutabilité intérieure non synchronisée), les pointeurs bruts. Chacune est un type que Rust refuse de laisser traverser, précisément parce qu'il 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), Cell and RefCell (unsynchronized interior mutability), raw pointers. Each is a type Rust refuses to let cross, precisely because it would cause a race.