Por que Rust é o novo rei da programação de rede?
\n\nVocê já escreveu um servidor de rede em C ou C++ e pegou um segmentation fault por causa de um mutex esquecido? Ou lutou contra condições de corrida em Go, tentando entender por que uma goroutine "travou"? O Rust resolve esses problemas de uma vez por todas. Seu sistema de ownership e o modelo de concorrência "sem medo" (fearless concurrency) tornam a programação de rede em Rust não apenas segura, mas incrivelmente performática.
\n\nNeste artigo, vamos explorar como criar servidores TCP e UDP em Rust, usando tanto a biblioteca padrão (std::net) quanto a moderna pilha async baseada em Tokio. Você verá que Rust não é apenas uma linguagem para programação de sistemas, mas também uma excelente ferramenta para escrever serviços de rede de alta carga.
Fundamentos: std::net — servidor TCP echo em 5 minutos
\n\nVamos começar com o clássico — servidor echo. Ele aceita uma conexão TCP, lê os dados e os envia de volta ao cliente. Em Rust, isso é feito de forma surpreendentemente concisa.
\n\nuse std::io::{Read, Write};\nuse std::net::{TcpListener, TcpStream};\nuse std::thread;\n\nfn handle_client(mut stream: TcpStream) {\n let mut buffer = [0; 1024];\n loop {\n match stream.read(&mut buffer) {\n Ok(0) => {\n println!("Cliente desconectou");\n break;\n }\n Ok(n) => {\n if let Err(e) = stream.write_all(&buffer[..n]) {\n eprintln!("Erro ao escrever: {}", e);\n break;\n }\n }\n Err(e) => {\n eprintln!("Erro ao ler: {}", e);\n break;\n }\n }\n }\n}\n\nfn main() -> std::io::Result<()> {\n let listener = TcpListener::bind("127.0.0.1:7878")?;\n println!("Servidor iniciado na porta 7878");\n\n for stream in listener.incoming() {\n match stream {\n Ok(stream) => {\n thread::spawn(|| handle_client(stream));\n }\n Err(e) => {\n eprintln!("Erro de conexão: {}", e);\n }\n }\n }\n Ok(())\n}\n\n\nO que está acontecendo aqui? TcpListener::bind cria um socket de escuta. O método incoming() retorna um iterador sobre as conexões recebidas. Para cada novo cliente, iniciamos uma thread separada — thread::spawn. Dentro de handle_client, lemos os dados para um buffer e os enviamos de volta via write_all.
Importante: Rust garante que TcpStream não será acidentalmente usado por duas threads simultaneamente — o compilador simplesmente não permite. Isso é o fearless concurrency.
Problemas dessa abordagem
\n- \n
- Escalabilidade: cada cliente é uma thread separada. Com 10.000 clientes, o sistema operacional simplesmente trava. \n
- Recursos: a pilha de thread padrão tem 2 MB. 10.000 clientes = 20 GB de memória. \n
A programação assíncrona vem para ajudar.
\n\nRevolução Async: Tokio e async/await
\n\nRust não possui um runtime assíncrono embutido (como Go ou Node.js). Em vez disso, ele fornece os traits Future e as palavras-chave async/await, e a escolha do runtime é sua. O mais popular é o Tokio. Não é apenas uma biblioteca, é um ecossistema completo: TCP/UDP, E/S de arquivos, timers, canais e até seu próprio escalonador.
Vamos reescrever nosso servidor echo com Tokio.
\n\nServidor echo com Tokio
\nAdicione ao Cargo.toml:
[dependencies]\ntokio = { version = "1", features = ["full"] }\n\nAgora o código:
\n\nuse tokio::io::{AsyncReadExt, AsyncWriteExt};\nuse tokio::net::TcpListener;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n