Rust 1.100: O Tipo que Levou 10 Anos e 5 Tentativas para Existir
Dez anos. Cinco tentativas de estabilização. Uma reversão que assombrou a comunidade por quase uma década. E agora, no dia 12 de novembro de 2026, o never type (!) finalmente saiu do nightly e entrou no Rust estável. Se você programa em Rust, já usou ! sem saber. Se não programa, essa história de persistência absurda vai te fazer repensar o que “feature pronta” significa.
O Rust 1.100 não é só sobre o never type. Essa release trouxe a Allocator API estável (depois de 7 anos no limbo), extern "custom" para quem faz FFI em embedded, WebAssembly com suporte async em tier 2, e um bug silencioso que vai quebrar pipelines de CI no mundo inteiro. Eu explico.
O que é o never type (e por que você deveria se importar)
Antes de entender por que demorou 10 anos, vale entender o que o ! faz.
Em Rust, todo valor tem um tipo. Quando uma função retorna um i32, você sabe que vai receber um inteiro de 32 bits. Quando retorna String, uma string. Mas e quando a função nunca retorna?
fn panic_now() -> ! {
panic!("acabou");
}
fn loop_forever() -> ! {
loop {
// isso aqui roda pra sempre
}
}
O ! é o tipo que representa “isso aqui nunca vai produzir um valor”. Funções que chamam panic!(), process::exit(), ou entram em loop infinito retornam !. Parece esotérico, mas na prática isso aparece o tempo todo.
Pense no match:
let value: u32 = match some_result {
Ok(v) => v,
Err(_) => panic!("deu ruim"),
};
O braço do Err nunca produz um u32, ele faz panic. Sem o never type, o compilador precisa de mágica interna para aceitar que esse código é válido. Com ! estabilizado, a tipagem é explícita e consistente.
O caso do Result
Aqui é onde fica interessante de verdade. Imagina que você tem uma operação que nunca falha:
fn always_works() -> Result<String, !> {
Ok("sempre funciona".to_string())
}
O tipo Result diz: “isso retorna Ok ou Err, mas o Err é impossível”. O compilador sabe disso e pode otimizar. Antes do 1.100, você precisava usar Infallible (um enum vazio) como gambiarra. Agora, Infallible virou literalmente um alias para !:
// Antes (Rust < 1.100)
type Infallible = enum {}; // tipo separado
// Depois (Rust 1.100+)
type Infallible = !; // alias direto
Isso unifica o sistema de tipos e elimina uma classe inteira de conversões desnecessárias.
Por que demorou 10 anos (a saga completa)
A história do never type é, sinceramente, um caso de estudo em design de linguagens. Não é que ninguém sabia o que ! deveria fazer. O problema era o que acontece quando o compilador não consegue inferir o tipo.
Tentativa 1: estabilização no Rust 1.26 (2018)
Em 2018, o never type foi estabilizado junto com TryFrom. Parecia que tinha dado certo. Só que junto veio uma mudança de “fallback”: quando o compilador encontrava um tipo ambíguo que envolvia !, ele precisava decidir o que fazer. A decisão era mudar o fallback de () (unit) para !.
O problema? Código existente que dependia do fallback () quebrou. Um crater run (teste massivo contra todo o ecossistema) flaggeou 3.277 crates com problemas potenciais. A estabilização foi revertida em abril de 2018.
Tentativas 2, 3 e 4: o limbo
Nos anos seguintes, várias abordagens foram tentadas. Lints de compatibilidade, períodos de transição, edições diferentes com comportamentos diferentes. Cada tentativa esbarrava no mesmo dilema: mudar o fallback quebra código antigo, não mudar deixa o sistema de tipos inconsistente.
Tentativa 5: a solução definitiva (2024-2026)
A edição Rust 2024 foi o ponto de virada. Com ela, o fallback do never type mudou para ! na edição 2024. O plano era: edições antigas mantêm (), edições novas usam !, e eventualmente todas migram.
No Rust 1.100, a migração ficou completa. Todas as edições (2015, 2018, 2021 e 2024) agora usam ! como fallback. O contributor waffle trabalhou mais de dois anos nessa transição gradual.
| Tentativa | Ano | O que aconteceu | |
|---|---|---|---|
| ———– | —– | —————– | |
| 1 | 2018 | Estabilizado no 1.26, revertido por quebrar 3.277 crates | |
| 2 | 2019 | Lint de compatibilidade adicionado, insuficiente | |
| 3 | 2020 | Nova abordagem de fallback, sem consenso | |
| 4 | 2022 | Proposta de migração por edição, aprovada mas incompleta | |
| 5 | 2024-2026 | Edição 2024 com novo fallback, migração completa no 1.100 |
Isso mostra algo que eu respeito muito no Rust: eles preferem esperar 10 anos do que quebrar o ecossistema. Em outras linguagens (Python 2 para 3, alguém?), a transição foi bem menos elegante.
Allocator API: 7 anos de nightly finalmente acabaram
Se o never type é a estrela filosófica do 1.100, a Allocator API é a estrela prática. E eu digo isso porque essa feature vai mudar como muita gente escreve código de alta performance em Rust.
Até agora, se você queria usar um allocator customizado em Rust estável, suas opções eram:
- Usar nightly (e rezar pra não quebrar a cada atualização)
- Reimplementar tudo por conta própria com
unsafe - Chorar
A partir do 1.100, o trait Allocator é estável:
use std::alloc::Allocator;
// Criar um Vec com allocator customizado
let bump = BumpAllocator::new();
let mut data: Vec<u32, &BumpAllocator> = Vec::new_in(&bump);
data.push(42);
// Box com allocator customizado
let boxed: Box<str, &BumpAllocator> = Box::new_in("hello", &bump);
Quem se beneficia
| Caso de uso | Por que importa | |
|---|---|---|
| ————- | —————– | |
| Game engines | Alocação per-frame com bump allocators, zero fragmentação | |
| Parsers de AST | Arena allocation para árvores de sintaxe, desalocação em batch | |
| Sistemas embarcados | Controle total sobre onde a memória é alocada | |
| Databases | Pool de memória pré-alocada para buffers de I/O | |
| WebAssembly | Allocators otimizados para o modelo de memória linear do Wasm |
A API estabilizada é intencionalmente mínima: Box::new_in(), Vec::new_in(), Vec::with_capacity_in(), e o trait Allocator em si. Isso é proposital. O time do Rust escolheu estabilizar o MVP e expandir depois, em vez de tentar cobrir todos os casos de uma vez (lição aprendida com o never type, talvez).
Para quem trabalha com game engines em Rust (Bevy, gente?), isso é enorme. Alocação per-frame é um padrão fundamental em gamedev, e até agora exigia nightly ou gambiarras com unsafe. Agora é first-class citizen.
extern "custom": FFI para quem vive no bare metal
Essa feature é mais nichada, mas para quem trabalha com embedded, é um game changer. O extern "custom" permite declarar funções com calling conventions que o Rust não suporta nativamente.
#[unsafe(naked)]
extern "custom" fn __aeabi_uidivmod() {
// ARM EABI division intrinsic
core::arch::naked_asm!(
"push {{lr}}",
"bl __aeabi_uidiv",
"mls r1, r0, r2, r1",
"pop {{pc}}",
);
}
As restrições são rígidas: a função precisa ser naked, unsafe, e só pode ser chamada via inline assembly ou FFI. Você não vai usar isso no dia a dia. Mas se você está escrevendo firmware, drivers, ou interagindo com hardware accelerators, isso elimina a necessidade de escrever stubs em C só para satisfazer uma calling convention exótica.
WebAssembly: wasm32-wasip3 agora é Tier 2
O target wasm32-wasip3 subiu para Tier 2, o que significa que binários pré-compilados são distribuídos com cada release do Rust. WASI 0.3 traz o async component model, permitindo composição async-async entre módulos WebAssembly e cooperative threading.
Na prática, isso significa que você pode compilar código Rust async para Wasm e rodar em runtimes como Wasmtime com suporte nativo a I/O assíncrono. Sem hacks, sem polyfills.
# Compilar para wasm32-wasip3
rustup target add wasm32-wasip3
cargo build --target wasm32-wasip3
Para quem está construindo plugins, serverless functions, ou sistemas de composição de microserviços em Wasm, essa promoção para Tier 2 é o sinal verde para usar em produção.
O bug dos 3 dígitos (sim, isso vai quebrar seu CI)
Aqui vai um aviso que deveria estar em letras garrafais em todo blog post sobre o 1.100: Rust 1.100 é a primeira versão com 3 dígitos no minor version. E isso quebra comparação lexicográfica de strings.
# Isso dá errado
if [[ "1.100" > "1.9" ]]; then
echo "1.100 é mais novo" # NUNCA executa!
fi
# Porque lexicograficamente: "1.1" < "1.9"
# O "00" depois é ignorado na comparação posicional
Se o seu pipeline de CI faz algo como:
# ERRADO - comparação de string
- if: ${{ env.RUST_VERSION >= '1.80' }}
Vai funcionar até 1.99. No 1.100, “1.100” é lexicograficamente menor que “1.80” (porque “1” < "8" na segunda posição após o ponto). Seus checks vão falhar silenciosamente.
A correção é usar comparação numérica:
# Correto - parse inteiro
MINOR=$(rustc --version | grep -oP '1\.\K[0-9]+')
if [[ "$MINOR" -ge 100 ]]; then
echo "Rust 1.100+"
fi
Eu apostaria dinheiro que nas próximas semanas vamos ver uma enxurrada de issues no GitHub sobre isso. Se você mantém actions, Docker images, ou qualquer automação que checa a versão do Rust: vai lá agora e arruma.
Mais de 40 APIs estabilizadas
Além das features headline, o 1.100 estabilizou um volume impressionante de APIs menores. Algumas que merecem destaque:
DropGuard: cleanup sem boilerplate
use std::mem::DropGuard;
let guard = DropGuard::new(|| {
println!("cleanup executado, não importa como saímos daqui");
});
// ... código que pode fazer panic, return early, etc.
// O closure roda quando guard sai de escopo
Antes, você precisava criar uma struct com impl Drop só para executar cleanup. DropGuard resolve isso em uma linha.
conjure_zst: criação segura de ZSTs
use std::mem::conjure_zst;
// Criar um PhantomData ou outro ZST sem unsafe
let marker: PhantomData<String> = conjure_zst();
Para quem trabalha com tipos zero-sized (markers, phantom types), essa função elimina a necessidade de unsafe para criar instâncias.
Métodos úteis de String
let s = " hello world ";
let trimmed = s.trim_prefix(" hello"); // " world "
let toggled = true.toggle(); // false
trim_prefix e trim_suffix para strings, bool::toggle(), Box::map(), Rc::map(), Arc::map(): pequenas conveniências que reduzem boilerplate no dia a dia.
Cargo também evoluiu
O Cargo 1.100 trouxe três mudanças que afetam o workflow de todo mundo:
1. Aviso de dependências não usadas
warning: unused dependency `serde_json`
--> Cargo.toml:12:1
|
12 | serde_json = "1.0"
| ^^^^^^^^^^ this dependency is never used
Finalmente! O Cargo agora avisa quando você tem uma dependência no Cargo.toml que nenhum código importa. Quem nunca deixou aquele crate de teste no [dependencies] em vez de [dev-dependencies]?
2. Reorganização do diretório de build
O layout do target/ foi reorganizado para agrupar por etapa de build em vez de tipo de arquivo. Isso reduz colisões de cache em workspaces grandes e melhora a performance de builds incrementais.
3. min-publish-age: proteção contra supply chain attacks
# Cargo.toml
[dependencies]
some-crate = { version = "1.0", min-publish-age = "3d" }
Essa configuração diz ao Cargo: “só aceite versões dessa dependência que foram publicadas há pelo menos 3 dias”. É uma camada extra de proteção contra typosquatting e supply chain attacks. Se alguém publicar uma versão maliciosa, você tem um buffer de tempo para que a comunidade detecte antes de você instalar.
Considerando que já vimos ataques no npm (lembra do axios hackeado?), essa feature é extremamente bem-vinda.
Rust 1.100 no contexto do ecossistema
Essa release marca um momento simbólico para o Rust. Versão 1.100 significa que o Rust já teve 100 releases menores sem nunca precisar de um 2.0. Enquanto outras linguagens têm breaking changes a cada major version (Python 2→3, Perl 5→6, Angular 1→2), o Rust conseguiu evoluir radicalmente mantendo compatibilidade backward.
O modelo de edições é o que torna isso possível. Código escrito em Rust 2015 ainda compila no Rust 1.100. Crates de edições diferentes interoperam sem problemas. É um feito de engenharia que não recebe o crédito que merece.
Para quem está avaliando Rust para um projeto novo em 2026, essa release remove duas das maiores objeções históricas:
- “Preciso de allocator customizado mas não quero nightly”: resolvido
- “O never type é instável e causa friction com genéricos”: resolvido
Se a sua desculpa para não usar Rust era uma dessas, acabou o argumento.
O que vem pela frente
O roadmap do Rust para 2027 inclui generators (coroutines estáveis), async iterators, e polonius (o novo borrow checker que promete aceitar mais código válido). Com o never type e a Allocator API finalmente fora do caminho, o time pode focar nessas features que vão tornar Rust ainda mais produtivo.
Atualizar é simples:
rustup update stable
rustc --version
# rustc 1.100.0 (xxxxx 2026-11-12)
Só não esquece de checar seus scripts de CI antes. Aquele if version >= "1.80" pode te dar uma surpresa desagradável.













