Et si la difficulté venait du partage — qu'on cesserait alors de partager ?What if the difficulty came from sharing — which we'd then stop doing?
Le numéro précédent a rouvert le partage mutable, mais au prix d'un verrou, d'une contention à doser et d'un deadlock qu'aucune preuve ne couvre. C'est le terrain difficile de la concurrence — et il le reste, même en Rust. Les canaux retournent le problème au lieu de l'affronter : plutôt que plusieurs threads se disputant une même mémoire mutable, un thread envoie un message, un autre le reçoit. La devise, empruntée à Go et faite sienne par Rust : « ne communique pas en partageant la mémoire ; partage la mémoire en communiquant ». Et le mécanisme qui rend cela sûr, on le connaît déjà : la possession de la donnée est déplacée à travers le canal — le move du Vol 1. L'émetteur la perd, le récepteur la gagne. À aucun instant deux threads ne la tiennent ensemble. Le data race n'est pas interdit : il devient impossible à exprimer, parce qu'il n'y a plus rien à partager.The previous issue reopened shared mutation, but at the price of a lock, of contention to balance, and of a deadlock no proof covers. That's concurrency's hard ground — and it stays hard, even in Rust. Channels flip the problem instead of fighting it: rather than several threads contending over the same mutable memory, one thread sends a message, another receives it. The motto, borrowed from Go and made Rust's own: 'don't communicate by sharing memory; share memory by communicating'. And the mechanism that makes this safe, we already know: ownership of the data is moved through the channel — Vol 1's move. The sender loses it, the receiver gains it. At no instant do two threads hold it together. The data race isn't forbidden: it becomes inexpressible, because there's nothing left to share.
use std::sync::mpsc; use std::thread; let (tx, rx) = mpsc::channel(); // tx : l'émetteur · rx : le récepteur thread::spawn(move || { let message = String::from("résultat"); tx.send(message).unwrap(); // la possession de `message` ENTRE dans le canal }); // (move : ce thread possède tx et message) let recu = rx.recv().unwrap(); // …et RESSORT ici, côté récepteur println!("{recu}"); // un seul propriétaire à tout instant : rien n'est partagé
mpsc::channel() rend une paire : un émetteur (tx) et un récepteur (rx). send dépose une valeur dans le canal, recv la retire de l'autre côté. Ce qui circule n'est pas une référence partagée mais la valeur elle-même, dont la possession voyage. Compare au Arc<Mutex> du №2 : là, plusieurs threads pointaient vers une mémoire commune et se relayaient par un verrou ; ici, la donnée n'est jamais commune — elle passe de main en main, et une seule main la tient à la fois.mpsc::channel() returns a pair: a sender (tx) and a receiver (rx). send drops a value into the channel, recv pulls it out the other side. What flows isn't a shared reference but the value itself, whose ownership travels. Compare to №2's Arc<Mutex>: there, several threads pointed at common memory and took turns via a lock; here, the data is never common — it passes hand to hand, and only one hand holds it at a time.
« Avant de dégainer Arc<Mutex>, ai-je vraiment besoin de partager — ou puis-je transférer la possession par un message ? » Quand des threads se passent du travail (une file de tâches, un pipeline, des résultats à collecter), le canal est presque toujours plus simple et plus sûr que la mémoire partagée : pas de verrou, pas de section critique, pas de deadlock sur des données. On ne synchronise pas l'accès — on supprime le besoin de synchroniser.'Before reaching for Arc<Mutex>, do I truly need to share — or can I transfer ownership through a message?' When threads hand off work (a task queue, a pipeline, results to collect), the channel is almost always simpler and safer than shared memory: no lock, no critical section, no deadlock on data. You don't synchronize access — you remove the need to synchronize.
multi-producer, single-consumer. Plusieurs émetteurs peuvent alimenter le même canal (l'émetteur se clone) ; un seul récepteur en sort les messages. C'est le canal de la std, dans std::sync::mpsc. Le modèle couvre l'immense majorité des cas : N ouvriers qui rapportent à un collecteur. Pour plusieurs récepteurs (multi-consumer) ou des canaux plus rapides, on nommera crossbeam au chapitre 3 — mais le réflexe et la sûreté sont identiques.multi-producer, single-consumer. Several senders can feed the same channel (the sender clones); one receiver pulls the messages out. It's the std's channel, in std::sync::mpsc. The model covers the vast majority of cases: N workers reporting to one collector. For several receivers (multi-consumer) or faster channels, we'll name crossbeam in chapter 3 — but the reflex and the safety are identical.