Un canal es una estructura de paso de mensajes entre hilos.
Un Canal (Channel) es como una tubería o un río. Pones algo en un extremo (el transmisor) y ese algo viaja hasta salir por el otro extremo (el receptor).
Es una abstracción muy segura porque rompe la dependencia temporal: el que envía no necesita saber cuándo (o si) el receptor estará listo, y viceversa. Además, encaja perfectamente con el modelo de Ownership de Rust.
La librería std::sync::mpsc
En la librería estándar de Rust, los canales se encuentran en el módulo mpsc.
Estas siglas significan: Multiple Producer, Single Consumer.
Esto quiere decir que un canal en Rust puede tener muchos extremos que envían (muchos transmisores), pero solo un extremo que recibe (un único receptor). Imagina muchos afluentes desembocando en un solo río principal.
Crear un canal
Para crear un canal usamos mpsc::channel. Nos devuelve una tupla con dos elementos:
tx(Transmitter): El extremo para enviar.rx(Receiver): El extremo para recibir.
use std::sync::mpsc;
use std::thread;
fn main() {
// Creamos el canal
let (tx, rx) = mpsc::channel();
// Lanzamos un hilo y le pasamos el transmisor (tx) con 'move'
thread::spawn(move || {
let mensaje = String::from("¡Hola desde el hilo!");
tx.send(mensaje).unwrap(); // Enviamos el mensaje
});
// En el hilo principal, esperamos recibir algo
let recibido = rx.recv().unwrap(); // Bloquea hasta que llega el dato
println!("Recibido: {}", recibido);
}send y recv
tx.send(valor): Envía un valor por el tubo. Devuelve unResult. Si el receptor (rx) ha sido destruido (el hilo receptor se cerró),senddevolverá un error para avisarnos de que nadie nos escucha.rx.recv(): Bloquea la ejecución del hilo actual hasta que llega un valor. Si el transmisor (tx) cierra el canal sin enviar nada, devuelve un error.rx.try_recv(): No bloquea. Comprueba si hay algo ahora mismo. Si no, devuelve error inmediatamente y sigue ejecutando. Útil para hacer otras cosas mientras esperas.
Canales y ownership
Con los canales se ve muy bien el modelo de Rust. send recibe el valor por valor: si no implementa Copy, la propiedad se transfiere; si lo implementa, se envía una copia.
El hilo que envía (tx) pierde el dato. El hilo que recibe (rx) se convierte en el nuevo dueño.
Para valores movidos, esto evita que el emisor y el receptor accedan simultáneamente al mismo dato. Los canales no eliminan todos los errores lógicos de concurrencia, pero reducen la necesidad de memoria mutable compartida.
thread::spawn(move || {
let val = String::from("Dato importante");
tx.send(val).unwrap();
// ❌ Error: 'val' se ha movido al canal. Ya no podemos usarlo aquí.
// println!("Intento usar val: {}", val);
});El compilador te garantiza que una vez enviado, te olvidas de él.
Varios productores
Como el nombre MPSC indica, podemos tener múltiples emisores. Para ello, debemos clonar el transmisor (tx). El receptor (rx) no se puede clonar.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
// Clonamos el transmisor para dárselo a otro hilo
let tx1 = tx.clone();
// Hilo 1: Envía mensajes usando el clon
thread::spawn(move || {
let msgs = vec!["Hola", "desde", "hilo", "1"];
for msg in msgs {
tx1.send(msg).unwrap();
thread::sleep(Duration::from_millis(200));
}
});
// Hilo 2: Envía mensajes usando el original
thread::spawn(move || {
let msgs = vec!["Mensajes", "del", "hilo", "2"];
for msg in msgs {
tx.send(msg).unwrap();
thread::sleep(Duration::from_millis(200));
}
});
// Consumidor (Hilo principal):
// Podemos iterar sobre rx como si fuera un iterador infinito.
// El bucle termina cuando TODOS los tx (tx y tx1) se hayan cerrado (dropped).
for recibido in rx {
println!("Recibido: {}", recibido);
}
}La salida mostrará los mensajes de ambos hilos mezclados, dependiendo de cómo el sistema operativo planifique los hilos, pero todos llegarán seguros al receptor.
Nota sobre el cierre:
El iterador for recibido in rx solo termina cuando todos los transmisores han desaparecido.
Si el main conserva un tx sin usarlo, el receptor puede quedarse bloqueado indefinidamente porque el canal continúa abierto y aún podría llegar otro mensaje.