Rust — Artigo #50
O Futuro do Rust — Async Traits, GATs, Especialização e o Roadmap da Linguagem
Por Prof. Dr. Marcelo Fontes | Série: Dominando Rust em 1 Ano
Rust é uma linguagem viva. Cada edição — 2015, 2018, 2021 — consolidou avanços significativos. O pipeline de desenvolvimento é transparente: RFCs públicos, tracking issues abertas, implementações progressivas em nightly antes da estabilização em stable. Você pode acompanhar em tempo real o que está sendo construído.
Este artigo examina as fronteiras atuais da linguagem: o que acabou de ser estabilizado, o que está em nightly aguardando estabilização, e o que está sendo debatido para o futuro. Entender o roadmap não é só curiosidade — é saber onde investir energia de aprendizado e como desenhar sistemas que se beneficiarão das melhorias que vêm por aí.
O que chegou recentemente ao stable
Async fn em traits — estabilizado no Rust 1.75
// Antes do Rust 1.75 — async fn em traits era impossível
// sem workarounds ou crates como async-trait
// ANTES (workaround com async-trait):
// use async_trait::async_trait;
// #[async_trait]
// trait Repositorio {
// async fn buscar(&self, id: u64) -> Option<Usuario>;
// }
// AGORA — nativo e sem overhead de Box<dyn Future>:
use std::future::Future;
trait Repositorio {
async fn buscar(&self, id: u64) -> Option<Usuario>;
async fn salvar(&mut self, usuario: &Usuario) -> Result<(), ErroDb>;
async fn deletar(&mut self, id: u64) -> bool;
}
struct RepositorioPostgres {
pool: Pool,
}
impl Repositorio for RepositorioPostgres {
async fn buscar(&self, id: u64) -> Option<Usuario> {
// Implementação real consultaria o banco
let row = self.pool.query_one(
"SELECT * FROM usuarios WHERE id = $1",
&[&(id as i64)],
).await.ok()?;
Some(Usuario {
id,
nome: row.get("nome"),
email: row.get("email"),
})
}
async fn salvar(&mut self, usuario: &Usuario) -> Result<(), ErroDb> {
self.pool.execute(
"INSERT INTO usuarios (id, nome, email) VALUES ($1, $2, $3)
ON CONFLICT (id) DO UPDATE SET nome = $2, email = $3",
&[&(usuario.id as i64), &usuario.nome, &usuario.email],
).await.map(|_| ()).map_err(ErroDb::Postgres)
}
async fn deletar(&mut self, id: u64) -> bool {
self.pool.execute(
"DELETE FROM usuarios WHERE id = $1",
&[&(id as i64)],
).await.map(|n| n > 0).unwrap_or(false)
}
}
// A limitação ainda existente: async fn em traits não é
// object-safe por padrão. Para trait objects, use RPIT:
trait ServicoNotificacao {
fn notificar(
&self,
mensagem: &str,
) -> impl Future<Output = Result<(), ErroNotif>> + Send;
}
// Stubs
struct Usuario { id: u64, nome: String, email: String }
struct Pool;
impl Pool {
async fn query_one(&self, _: &str, _: &[&dyn std::fmt::Debug])
-> Result<Row, String> { Ok(Row) }
async fn execute(&self, _: &str, _: &[&dyn std::fmt::Debug])
-> Result<u64, PgError> { Ok(1) }
}
struct Row;
impl Row { fn get(&self, _: &str) -> String { String::new() } }
struct PgError;
#[derive(Debug)] enum ErroDb { Postgres(PgError) }
#[derive(Debug)] struct ErroNotif;
Return Position Impl Trait (RPIT) em traits — Rust 1.75
// Permite retornar tipos opacos em traits
trait Processador {
// Retorna um iterador sem nomear o tipo concreto
fn processar<'a>(
&'a self,
dados: &'a [u8],
) -> impl Iterator<Item = u8> + 'a;
// Retorna um Future opaco
fn processar_async(
&self,
dados: Vec<u8>,
) -> impl Future<Output = Vec<u8>> + Send + '_;
}
struct ProcessadorBase64;
impl Processador for ProcessadorBase64 {
fn processar<'a>(
&'a self,
dados: &'a [u8],
) -> impl Iterator<Item = u8> + 'a {
// O tipo do iterador é inferido — não precisa ser nomeado
dados.iter()
.map(|&b| if b.is_ascii_alphabetic() {
b.to_ascii_uppercase()
} else {
b
})
}
fn processar_async(
&self,
dados: Vec<u8>,
) -> impl Future<Output = Vec<u8>> + Send + '_ {
async move {
tokio::time::sleep(std::time::Duration::from_millis(1)).await;
dados.iter().map(|&b| b.wrapping_add(1)).collect()
}
}
}
GATs — Generic Associated Types — Rust 1.65
// GATs permitem tipos associados que são genéricos
// sobre lifetimes ou outros parâmetros de tipo
// Sem GATs — impossível expressar este padrão:
trait ColecaoSemGat {
type Item;
// Como retornar uma referência com lifetime ligado ao self?
// fn iter(&self) -> impl Iterator<Item = &Self::Item>; // não funcionava
}
// COM GATs — expressivo e preciso:
trait Colecao {
type Item;
// GAT: o tipo de retorno é genérico sobre o lifetime 'a
type Iter<'a>: Iterator<Item = &'a Self::Item>
where
Self: 'a;
fn iter<'a>(&'a self) -> Self::Iter<'a>;
}
struct VecColecao<T>(Vec<T>);
impl<T> Colecao for VecColecao<T> {
type Item = T;
type Iter<'a> = std::slice::Iter<'a, T>
where
T: 'a;
fn iter<'a>(&'a self) -> Self::Iter<'a> {
self.0.iter()
}
}
// GATs para streaming — padrão muito útil:
trait StreamingIterator {
type Item<'a> where Self: 'a;
fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}
struct LinhasDoCsv {
buffer: String,
linhas: Vec<String>,
posicao: usize,
}
impl StreamingIterator for LinhasDoCsv {
// O item empresta do iterador, não do caller
type Item<'a> = &'a str where Self: 'a;
fn next<'a>(&'a mut self) -> Option<Self::Item<'a>> {
if self.posicao < self.linhas.len() {
let linha = &self.linhas[self.posicao];
self.posicao += 1;
Some(linha.as_str())
} else {
None
}
}
}
Em nightly — caminho para estabilização
Especialização (Specialization)
// #![feature(specialization)] — nightly, há anos em debate
// Permite implementações mais específicas sobrescrever as gerais
#![feature(min_specialization)]
// Exemplo: implementação padrão e especializada
trait Serializar {
fn serializar(&self) -> String;
}
// Implementação geral para qualquer Debug
impl<T: std::fmt::Debug> Serializar for T {
default fn serializar(&self) -> String {
format!("{:?}", self)
}
}
// Especialização para String — mais eficiente
impl Serializar for String {
fn serializar(&self) -> String {
format!(""{}"", self.replace('"', "\""))
}
}
// Especialização para números — sem aspas
impl Serializar for i64 {
fn serializar(&self) -> String {
self.to_string()
}
}
// Uso real: a impl certa é chamada automaticamente
fn demo_especializacao() {
let s = String::from("hello "world"");
let n: i64 = 42;
let v = vec![1, 2, 3];
println!("{}", s.serializar()); // "hello "world""
println!("{}", n.serializar()); // 42
println!("{}", v.serializar()); // [1, 2, 3]
}
// Por que está demorando tanto para estabilizar?
// O problema da soundness com lifetimes:
//
// impl<T: Clone> Processar for T {
// default fn processar(&self) -> String { ... }
// }
// impl Processar for &str {
// fn processar(&self) -> String { ... }
// }
// Mas: impl<'a> Processar for &'a str pode ser mais geral
// que impl Processar for &'static str — o compilador pode
// escolher a impl errada em alguns casos com lifetimes.
// A solução (min_specialization) restringe os casos permitidos.
Async closures
// #![feature(async_closure)] — nightly, Rust 2024 Edition
// Closures que podem usar .await internamente
// ANTES — workaround verbose:
fn processar_itens_antes<T, F, Fut>(
itens: Vec<T>,
f: F,
) -> impl Future<Output = Vec<String>>
where
F: Fn(T) -> Fut,
Fut: Future<Output = String>,
{
async move {
let mut resultados = Vec::new();
for item in itens {
resultados.push(f(item).await);
}
resultados
}
}
// COM async closures — muito mais natural:
async fn processar_itens<T: Send>(
itens: Vec<T>,
f: impl async Fn(T) -> String,
) -> Vec<String> {
let mut resultados = Vec::new();
for item in itens {
resultados.push(f(item).await);
}
resultados
}
// Uso:
async fn exemplo_async_closure() {
let nums = vec![1u32, 2, 3, 4, 5];
let resultados = processar_itens(nums, async |n| {
// Pode usar await diretamente na closure!
tokio::time::sleep(std::time::Duration::from_millis(n as u64)).await;
format!("resultado_{n}")
}).await;
println!("{:?}", resultados);
}
// Por que async closures são complexas de implementar?
// Uma closure captura por referência por padrão.
// Uma async closure retorna um Future que pode viver mais
// que a closure original. Reconciliar esses lifetimes
// com a semântica de captura requereu design cuidadoso.
Polonius — novo borrow checker
// O borrow checker atual (NLL) às vezes rejeita código correto.
// Polonius é uma reformulação baseada em Datalog que é
// mais precisa e aceita todos os programas que NLL aceita
// mais alguns programas corretos que NLL rejeita.
// Exemplo clássico que NLL rejeita mas Polonius aceita:
fn obter_ou_inserir<'a>(
mapa: &'a mut std::collections::HashMap<String, String>,
chave: &str,
) -> &'a str {
// Com NLL, isso às vezes falha com erro de borrow
// mesmo sendo correto — o borrow de `mapa` em `get`
// parece conflitar com o borrow em `insert`
if let Some(valor) = mapa.get(chave) {
return valor; // emprestado de mapa
}
// NLL pensa que o borrow imutável de `get` ainda está ativo
// Polonius entende que ele terminou no `return` acima
mapa.insert(chave.to_string(), "padrão".to_string());
mapa.get(chave).unwrap()
}
// Ativa Polonius em nightly:
// RUSTFLAGS="-Z polonius" cargo +nightly build
// Polonius também habilita análise mais precisa de lifetimes
// em closures capturando partes de structs:
struct Config {
host: String,
porta: u16,
opcoes: std::collections::HashMap<String, String>,
}
fn processar_config(config: &mut Config) {
let host = &config.host; // borrow de um campo
// Com NLL, isso pode falhar porque `config` está emprestado
// Com Polonius, o compilador entende que apenas `host`
// (não `opcoes`) está emprestado
config.opcoes.insert("chave".to_string(), host.clone());
}
Ergonomias de pattern matching
// Várias melhorias de ergonomia chegando/chegaram:
// 1. or-patterns — estabilizado no Rust 1.53
let x = Some(3);
match x {
Some(1 | 2 | 3) => println!("pequeno"),
Some(n) => println!("outro: {n}"),
None => println!("nenhum"),
}
// 2. if let chains — estabilizado no Rust 1.64
fn processar(opt_a: Option<i32>, opt_b: Option<i32>) {
if let Some(a) = opt_a
&& let Some(b) = opt_b
&& a + b > 10
{
println!("Soma {}: maior que 10", a + b);
}
}
// 3. let-else — estabilizado no Rust 1.65
fn parsear_porta(s: &str) -> Option<u16> {
let Ok(n) = s.parse::<u16>() else {
return None;
};
// n está disponível aqui como u16
Some(n)
}
// 4. Binding modes melhorados — em discussão
// Atualmente:
fn exemplo_binding_atual(opt: &Option<String>) {
if let Some(s) = opt {
// s é &String — binding mode automático
println!("{s}");
}
}
// 5. Deref patterns — em RFC/nightly
// Permite fazer match em Box<T>, Rc<T>, etc. diretamente:
// match boxed_value {
// box 42 => println!("quarenta e dois"),
// box n => println!("outro: {n}"),
// }
O que está sendo debatido para o futuro
Efeitos e capacidades (Effects/Capabilities)
// Uma das discussões mais importantes do momento.
// Rust tem vários "modificadores" que se combinam de forma
// inconsistente: async, unsafe, const, ?Trait...
// A visão: um sistema unificado de "efeitos" onde:
// Atual (inconsistente):
async fn operacao_async() -> u32 { 42 }
unsafe fn operacao_unsafe() -> u32 { 42 }
const fn operacao_const() -> u32 { 42 }
// Impossível hoje (função genérica sobre ser async ou não):
// fn pode_ser_async_ou_nao?() -> u32 { ... }
// Com effects (visão futura):
// fn generico<effect E>() -> u32
// where
// E: maybeasync + maybeunsafe
// { ... }
// Por que isso importa?
// Imagine uma crate de parsing que quer ser:
// - síncrona para uso simples
// - assíncrona para I/O
// - const para uso em tempo de compilação
// Hoje você precisaria de três implementações separadas.
// Com effects, uma só implementação serviria a todos.
// Isso está em fase de RFC/discussão — anos até estabilizar.
// Mas molda muitas decisões de design tomadas hoje.
const generics avançados
// O que já funciona (Rust 1.51+):
fn array_de_zeros<const N: usize>() -> [f64; N] {
[0.0; N]
}
struct MatrizFixa<T, const LINHAS: usize, const COLS: usize> {
dados: [[T; COLS]; LINHAS],
}
impl<T: Copy + Default, const L: usize, const C: usize>
MatrizFixa<T, L, C>
{
fn novo() -> Self {
MatrizFixa {
dados: [[T::default(); C]; L],
}
}
fn get(&self, linha: usize, col: usize) -> Option<&T> {
self.dados.get(linha)?.get(col)
}
}
// O que está chegando — const expressions mais ricas:
// #![feature(generic_const_exprs)] — nightly, instável
// Permite expressões aritméticas em const generics:
// fn concatenar_arrays<const N: usize, const M: usize>(
// a: [u8; N],
// b: [u8; M],
// ) -> [u8; N + M] { // N + M como const generic!
// ...
// }
// Permite const em bounds:
// fn requer_pelo_menos_dois<T, const N: usize>(arr: [T; N])
// where
// [(); N - 2]:, // garante N >= 2 em compile time
// {
// ...
// }
// Por que é difícil?
// O type checker precisa provar propriedades aritméticas.
// 2 + 2 == 4 é trivial. N + M == M + N requer comutatividade.
// Casos com variáveis são indecidíveis em geral.
Rust 2024 Edition
// Edições do Rust não são versões — são "lentes" que
// permitem mudanças de sintaxe sem quebrar código antigo.
// Todo código Rust, de toda edição, compila junto.
// Mudanças confirmadas na Rust 2024 Edition:
// 1. RPIT lifetime capture mais preciso
// Rust 2021:
fn iter_2021(dados: &[u32]) -> impl Iterator<Item = u32> {
// Implicitamente captura o lifetime de `dados`
dados.iter().copied()
}
// Rust 2024:
fn iter_2024(dados: &[u32]) -> impl Iterator<Item = u32> + use<'_> {
// Explícito sobre quais lifetimes são capturados
dados.iter().copied()
}
// 2. gen blocks (geradores/coroutines) — em discussão para 2024
// Permite escrever iteradores de forma imperativa:
// fn fibonacci() -> impl Iterator<Item = u64> {
// gen {
// let (mut a, mut b) = (0u64, 1u64);
// loop {
// yield a;
// (a, b) = (b, a + b);
// }
// }
// }
// 3. async gen — iteradores assíncronos com sintaxe natural
// async fn stream_de_eventos() -> impl Stream<Item = Evento> {
// async gen {
// loop {
// let evento = aguardar_proximo_evento().await;
// yield evento;
// }
// }
// }
// 4. Mudanças em match ergonomics
// Tornar o comportamento mais consistente e previsível
// 5. Migração mais suave de unsafe
// Novos avisos para padrões unsafe que serão erros no futuro
Tendências do ecossistema
Async Rust em maturação
// O ecossistema async está convergindo após anos de fragmentação
// tokio — runtime dominante para servidores
// smol/async-std — alternativas mais leves
// embassy — async para embedded (sem OS)
// A padronização que está chegando:
// 1. AsyncIterator (antes Stream) — na stdlib
// use std::async_iter::AsyncIterator;
// Permitirá: for await item in stream { ... }
// 2. AsyncRead/AsyncWrite na stdlib
// Hoje: tokio::io::{AsyncRead, AsyncWrite}
// futures::io::{AsyncRead, AsyncWrite}
// São incompatíveis! Stdlib resolveria isso.
// 3. AsyncDrop — complexo, em discussão
// Permite .await em destructors — necessário para
// conexões de banco que precisam enviar FIN assíncrono
// Código atual que vai melhorar:
use tokio::io::{AsyncReadExt, AsyncWriteExt};
async fn copiar_com_buffer(
mut leitor: impl tokio::io::AsyncRead + Unpin,
mut escritor: impl tokio::io::AsyncWrite + Unpin,
) -> std::io::Result<u64> {
let mut buffer = vec![0u8; 64 * 1024];
let mut total = 0u64;
loop {
let n = leitor.read(&mut buffer).await?;
if n == 0 { break; }
escritor.write_all(&buffer[..n]).await?;
total += n as u64;
}
Ok(total)
}
Rust no kernel Linux
// Desde o kernel 6.1 (dezembro 2022), Rust é suportado
// oficialmente como segunda linguagem no kernel Linux.
// Módulos de kernel em Rust — exemplo simplificado:
// (código real usa infraestrutura do kernel)
// use kernel::prelude::*;
// use kernel::{Module, ThisModule, file};
//
// module! {
// type: MeuModulo,
// name: "meu_modulo",
// author: "Desenvolvedor",
// description: "Módulo de exemplo em Rust",
// license: "GPL",
// }
//
// struct MeuModulo;
//
// impl kernel::Module for MeuModulo {
// fn init(_name: &'static CStr, _module: &'static ThisModule)
// -> Result<Self>
// {
// pr_info!("Módulo Rust inicializado
");
// Ok(MeuModulo)
// }
// }
//
// impl Drop for MeuModulo {
// fn drop(&mut self) {
// pr_info!("Módulo Rust descarregado
");
// }
// }
// Por que isso importa?
// O kernel Linux roda em todo servidor, smartphone Android,
// e dispositivo IoT. Rust no kernel significa:
// - Drivers novos podem ser escritos em Rust
// - Menor superfície de CVEs por memory safety
// - Pressão para Rust suportar ambientes no-std avançados
// - Validação definitiva de Rust como linguagem de sistemas
Rastreando o progresso
# Fontes para acompanhar o desenvolvimento do Rust:
# 1. Blog oficial — anúncios de releases
# https://blog.rust-lang.org
# 2. Inside Rust — posts do time de desenvolvimento
# https://blog.rust-lang.org/inside-rust/
# 3. This Week in Rust — curadoria semanal
# https://this-week-in-rust.org
# 4. Tracking issues — o que está em desenvolvimento
# https://github.com/rust-lang/rust/labels/C-tracking-issue
# 5. Unstable Book — features em nightly documentadas
# https://doc.rust-lang.org/unstable-book/
# 6. Rust Roadmap
# https://github.com/rust-lang/rust/milestone/
# Como experimentar features de nightly com segurança:
# rust-toolchain.toml — fixa a toolchain por projeto
cat > rust-toolchain.toml << 'EOF'
[toolchain]
channel = "nightly-2024-01-15" # data específica — reprodutível
components = ["rustfmt", "clippy", "miri"]
targets = ["wasm32-unknown-unknown"]
EOF
# Ativa feature apenas para um módulo
// src/experimental.rs
#![cfg_attr(feature = "nightly", feature(async_closure))]
#![cfg_attr(feature = "nightly", feature(gen_blocks))]
// Cargo.toml
// [features]
// nightly = []
Um olhar panorâmico: o que Rust provou
// Ao longo desta série de 50 artigos, construímos sistemas em:
// - Web (APIs, WebSockets, WebAssembly)
// - Sistemas distribuídos (consenso, mensageria)
// - Embedded (sem SO, bare metal)
// - Compiladores e interpretadores
// - Blockchain e criptografia
// - Machine learning (inferência, treinamento)
// - GameDev (ECS, Bevy)
// - CLIs profissionais
// - Sistemas operacionais (conceitos)
// O que Rust provou ao longo desses domínios:
// 1. SEGURANÇA SEM CUSTO DE RUNTIME
// Sem GC, sem overhead, sem pausas.
// Performance de C com segurança de linguagens modernas.
// 2. EXPRESSIVIDADE DE ALTO NÍVEL
// Iteradores, closures, traits, generics —
// a abstração zero-cost não é só marketing.
// 3. CONFIABILIDADE EM ESCALA
// "If it compiles, it often works" não é mito.
// O compilador pega classes de bugs que outros sistemas
// só encontram em produção.
// 4. ECOSSISTEMA MADURO
// Cargo é o melhor gerenciador de pacotes de qualquer linguagem.
// crates.io tem soluções de qualidade para quase todo domínio.
// 5. COMUNIDADE EXCEPCIONAL
// Rust foi eleita a linguagem "mais amada" por 8 anos
// consecutivos no Stack Overflow Developer Survey (2016-2023).
// Isso não é coincidência — é o resultado de uma cultura
// que valoriza tanto correctness quanto experiência do desenvolvedor.
// O que Rust ainda está construindo:
// - Async mais ergonômico (closures, iteradores, drop)
// - Especialização estável
// - Effects system para composição de capacidades
// - Melhor suporte a reflexão (limitada por design)
// - Compilação incremental ainda mais rápida
// - Diagnósticos de erro ainda mais claros
Fontes e leituras recomendadas
- Rust Blog — anúncios oficiais — https://blog.rust-lang.org
- Inside Rust Blog — desenvolvimento interno — https://blog.rust-lang.org/inside-rust/
- Rust Unstable Book — features em nightly — https://doc.rust-lang.org/unstable-book/
- RFC Repository — propostas em discussão — https://github.com/rust-lang/rfcs
- Tracking Issues — o que está sendo implementado — https://github.com/rust-lang/rust/labels/C-tracking-issue
- "Polonius: The Next-Generation Borrow Checker" — https://blog.rust-lang.org/2022/08/05/nll-by-default.html
- "Async Rust in 2024" — https://without.boats/blog/
- Rust Zulip — conversa em tempo real com o time — https://rust-lang.zulipchat.com
- "The Rust Performance Book" — https://nnethercote.github.io/perf-book/
Artigo #50 de 52 | Série: Dominando Rust em 1 Ano Próximo → Artigo #51: Projetos Integradores — Construindo sistemas completos do zero