Introdução
As versões 1.86 e 1.87 do Rust trouxeram recursos que mudam a forma como escrevemos código no dia a dia. Desde o esperado trait upcasting — que simplifica drasticamente o trabalho com trait objects — até pipes anônimos na stdlib e a possibilidade de chamar intrínsecos de arquitetura em código seguro, essas releases consolidam a missão do Rust: oferecer poder sem sacrificar segurança.
Neste artigo, vamos explorar cada recurso com exemplos de código prontos para você testar. Se você ainda não atualizou, basta rodar:
rustup update stable
Se quiser entender como instalar o Rust do zero, confira nosso guia de instalação.
Rust 1.86: Os Destaques
Trait Upcasting
O recurso mais aguardado do Rust 1.86 é, sem dúvida, o trait upcasting. Antes desta versão, converter uma referência &dyn Trait para &dyn Supertrait exigia gambiarras manuais — métodos auxiliares como fn as_supertrait(&self) -> &dyn Supertrait. Agora, a conversão é automática:
trait Animal {
fn nome(&self) -> &str;
}
trait Cachorro: Animal {
fn latir(&self) -> String;
}
struct Labrador {
nome: String,
}
impl Animal for Labrador {
fn nome(&self) -> &str {
&self.nome
}
}
impl Cachorro for Labrador {
fn latir(&self) -> String {
format!("{} diz: Au au!", self.nome())
}
}
fn imprimir_animal(animal: &dyn Animal) {
println!("Animal: {}", animal.nome());
}
fn main() {
let rex: Box<dyn Cachorro> = Box::new(Labrador {
nome: "Rex".to_string(),
});
// Antes do 1.86: erro de compilação!
// Agora: conversão automática de &dyn Cachorro → &dyn Animal
imprimir_animal(&*rex);
// Também funciona com Arc e Rc
let rex_arc: std::sync::Arc<dyn Cachorro> = std::sync::Arc::new(Labrador {
nome: "Rex".to_string(),
});
let animal_arc: std::sync::Arc<dyn Animal> = rex_arc;
println!("Via Arc: {}", animal_arc.nome());
}
Se você trabalha com trait objects e generics, esse recurso elimina uma enorme fonte de boilerplate. Combinado com smart pointers como Arc e Rc, o trait upcasting torna padrões de polimorfismo dinâmico muito mais ergonômicos.
Para um guia completo sobre traits, confira nosso artigo sobre traits em Rust.
get_disjoint_mut: Múltiplas Referências Mutáveis Seguras
Um dos problemas clássicos do Rust é acessar dois elementos mutáveis de um slice ou HashMap ao mesmo tempo. O compilador impede porque não consegue provar em tempo de compilação que os índices são diferentes. O novo método get_disjoint_mut resolve isso:
fn main() {
let mut valores = vec![10, 20, 30, 40, 50];
// Antes: split_at_mut ou unsafe
// Agora: get_disjoint_mut!
if let Ok([a, c]) = valores.get_disjoint_mut([0, 2]) {
*a += 100;
*c += 200;
println!("a={}, c={}", a, c); // a=110, c=230
}
println!("valores: {:?}", valores);
}
Isso funciona também com HashMap:
use std::collections::HashMap;
fn main() {
let mut mapa = HashMap::new();
mapa.insert("x", 1);
mapa.insert("y", 2);
mapa.insert("z", 3);
if let Ok([vx, vz]) = mapa.get_disjoint_mut(["x", "z"]) {
*vx *= 10;
*vz *= 10;
}
println!("{:?}", mapa); // {"x": 10, "y": 2, "z": 30}
}
Se você usa Vec ou HashMap no dia a dia, esse método elimina a necessidade de unsafe ou split_at_mut para o caso mais comum de acesso simultâneo.
#[target_feature] em Funções Seguras
Antes do 1.86, usar #[target_feature] exigia que a função fosse unsafe. Agora, funções seguras podem ser marcadas com features de CPU específicas:
#[target_feature(enable = "avx2")]
fn soma_vetorial_avx2(a: &[f32; 8], b: &[f32; 8]) -> [f32; 8] {
let mut resultado = [0.0f32; 8];
for i in 0..8 {
resultado[i] = a[i] + b[i];
}
resultado
}
fn main() {
if std::is_x86_feature_detected!("avx2") {
let a = [1.0; 8];
let b = [2.0; 8];
let resultado = soma_vetorial_avx2(&a, &b);
println!("Resultado: {:?}", resultado);
} else {
println!("AVX2 não disponível neste processador");
}
}
Para quem trabalha com otimização de performance, isso reduz a quantidade de unsafe no código sem perder controle sobre instruções SIMD.
Asserções de Ponteiro Nulo em Debug
O compilador agora insere verificações automáticas em modo debug para detectar desreferência de ponteiros nulos antes de qualquer leitura ou escrita de tamanho diferente de zero. Se você usa ponteiros e unsafe, isso ajuda a pegar bugs mais cedo durante o desenvolvimento.
Rust 1.87: Os Destaques
io::pipe — Pipes Anônimos na Stdlib
Finalmente temos pipes nativos na biblioteca padrão! O std::io::pipe() cria um par leitor/escritor que funciona tanto no Unix quanto no Windows:
use std::io::{self, Read, Write};
use std::thread;
fn main() -> io::Result<()> {
let (mut reader, mut writer) = io::pipe()?;
let handle = thread::spawn(move || {
writer.write_all(b"Olá do Rust 1.87!").unwrap();
writer.write_all(b" Pipes nativos!").unwrap();
// writer é dropado aqui, sinalizando EOF
});
let mut buffer = String::new();
reader.read_to_string(&mut buffer)?;
println!("Recebido: {}", buffer);
handle.join().unwrap();
Ok(())
}
Para quem trabalha com threads e canais de comunicação, io::pipe oferece uma alternativa baseada em I/O que se integra bem com processos filhos e redirects de stdin/stdout.
Intrínsecos de Arquitetura Seguros
A maioria dos intrínsecos em std::arch agora pode ser chamada sem unsafe quando as features de CPU apropriadas estão habilitadas. Isso é uma revolução para código SIMD:
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse4.1")]
fn soma_inteiros_sse(a: &[i32; 4], b: &[i32; 4]) -> [i32; 4] {
use std::arch::x86_64::*;
// Antes: unsafe { _mm_add_epi32(...) }
// Agora: chamada segura!
let va = unsafe { _mm_loadu_si128(a.as_ptr() as *const _) };
let vb = unsafe { _mm_loadu_si128(b.as_ptr() as *const _) };
let resultado = unsafe { _mm_add_epi32(va, vb) };
let mut out = [0i32; 4];
unsafe { _mm_storeu_si128(out.as_mut_ptr() as *mut _, resultado) };
out
}
Vec::extract_if — Filtrar e Remover Elementos
O método extract_if permite filtrar e remover elementos de um Vec em uma única passagem, retornando os elementos removidos como iterador:
fn main() {
let mut numeros = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Extrai todos os pares (remove do vec e retorna como iterador)
let pares: Vec<i32> = numeros.extract_if(.., |x| *x % 2 == 0).collect();
println!("Pares removidos: {:?}", pares); // [2, 4, 6, 8, 10]
println!("Ímpares restantes: {:?}", numeros); // [1, 3, 5, 7, 9]
}
Antes, você precisava de retain (que descarta os removidos) ou loops manuais. Agora, com extract_if, você mantém ambos os conjuntos. Muito útil para filtrar coleções em processamento de dados.
String::extend_from_within
Novo método para duplicar partes de uma String sem alocar strings intermediárias:
fn main() {
let mut texto = String::from("Rust");
texto.extend_from_within(..);
println!("{}", texto); // "RustRust"
let mut saudacao = String::from("Olá, mundo!");
saudacao.extend_from_within(0..4);
println!("{}", saudacao); // "Olá, mundo!Olá,"
}
Qual Versão Usar?
Se você está começando agora, use sempre a versão estável mais recente. Nosso guia Como Aprender Rust em 2026 tem o caminho completo. Para atualizar:
rustup update stable
rustc --version
Resumo das Novidades
| Recurso | Versão | Impacto |
|---|---|---|
| Trait upcasting | 1.86 | Polimorfismo dinâmico mais ergonômico |
get_disjoint_mut | 1.86 | Acesso mutável simultâneo seguro |
#[target_feature] safe | 1.86 | Menos unsafe em código SIMD |
| Null pointer assertions | 1.86 | Debug mais seguro |
io::pipe | 1.87 | Pipes nativos na stdlib |
| Safe arch intrinsics | 1.87 | SIMD sem unsafe desnecessário |
Vec::extract_if | 1.87 | Filtrar + remover em uma passagem |
String::extend_from_within | 1.87 | Duplicação eficiente de substrings |
Leia Também
- Rust 1.94: Novidades e Recursos Estabilizados — mais novidades recentes
- Traits em Rust: Guia Completo — fundamentos de traits
- Tratamento de Erros com thiserror e anyhow — boas práticas de error handling
- Testes em Rust: Estratégias e Boas Práticas — como testar seu código
- Cargo Workspaces: Organizando Projetos Grandes em Rust — gerenciando monorepos
Se você vem de outra linguagem, confira também como Rust se compara: Go, Python, Kotlin e Zig são linguagens que frequentemente aparecem em comparações com Rust.