usize vs u32: duelo de tipos inteiros em Rust
Se você programa em Rust há pelo menos uma semana, com certeza já passou por esta situação: o compilador reclama porque você está tentando usar u32 para indexar um vetor, e ele exige usize. Ou o contrário — você passa usize para uma função que espera u32 e recebe uma onda vermelha na IDE.
No Stack Overflow, a pergunta «What's the difference between usize and u32?» já teve milhares de visualizações. Não é por acaso — a confusão entre esses tipos é uma das armadilhas mais comuns para iniciantes em Rust.
Vamos entender de uma vez por todas como eles diferem, quando usar cada um e como não cometer erros. Vamos lá!
Resumindo: a principal diferença
usize é um inteiro sem sinal cujo tamanho depende da arquitetura do sistema. Em um sistema de 64 bits, são 8 bytes; em um de 32 bits, são 4 bytes. u32 tem sempre 4 bytes, independentemente da plataforma.
Mas a diferença não é apenas no tamanho. O principal é a semântica:
usize— para tamanhos, índices e quantidade de elementos em coleçõesu32— para dados numéricos que não ultrapassem 4 bilhões
E sim, o compilador não permite misturá-los sem uma conversão explícita. Isso não é um bug, é uma funcionalidade — assim o Rust protege você de erros relacionados à largura da arquitetura.
Por que usize existe?
Imagine que você está escrevendo um código que deve funcionar tanto em um microcontrolador antigo de 32 bits quanto em um servidor moderno de 64 bits. O comprimento de um vetor em um sistema de 32 bits não pode exceder 2^32 - 1 elementos, enquanto em um de 64 bits pode chegar a 2^64 - 1.
Se você usasse u32 para indexação em um sistema de 64 bits, simplesmente não conseguiria acessar elementos além de 4 bilhões — mesmo que houvesse memória suficiente. usize resolve esse problema: ele sempre comporta o tamanho máximo de memória possível na arquitetura atual.
// Exemplo: usize se ajusta automaticamente à arquiteturafn main() { let large_vec: Vec<u8> = vec![0; 1_000_000_000]; // 1 bilhão de elementos // usize comporta esse índice em qualquer plataforma let index: usize = 999_999_999; println!("Elemento: {}", large_vec[index]); // Já u32 pode ser insuficiente em um sistema de 32 bits // let bad_index: u32 = 999_999_999; // Em um sistema de 32 bits, isso compilaria, mas... // large_vec[bad_index as usize]; // ...seria necessário fazer a conversão}Comparação em tabela
| Característica | usize | u32 |
|---|---|---|
| Tamanho em 64-bit | 8 bytes | 4 bytes |
| Tamanho em 32-bit | 4 bytes | 4 bytes |
| Faixa (64-bit) | 0 .. 2^64-1 | 0 .. 2^32-1 |
| Finalidade principal | Índices, tamanhos, comprimentos | Dados numéricos |
| Conversão implícita | Não | Não |
Quando usar usize?
Sempre para indexação de coleções. Esta é uma regra praticamente sem exceções. Se você escrever vec[i], onde i é um u32, o compilador dirá: "Ei, amigo, use usize".
// Correto: usando usize para índicesfn get_element(vec: &[i32], index: usize) -> Option<&i32> { vec.get(index)}
// Incorreto: u32 para indexação — será necessário converterfn get_ele