usize vs u32: duelo de tipos enteros en Rust
Si llevas al menos una semana programando en Rust, seguro te has encontrado con esta situación: el compilador se queja porque intentas usar u32 para indexar un vector, y él requiere usize. O al revés: pasas un usize a una función que espera u32 y obtienes una ola roja en el IDE.
En Stack Overflow, la pregunta «What's the difference between usize and u32?» ha acumulado miles de visitas. No es casualidad: la confusión entre estos tipos es una de las trampas más comunes para los principiantes en Rust.
Aclaremos de una vez por todas en qué se diferencian, cuándo usar cada uno y cómo no meter la pata. ¡Allá vamos!
En resumen: la diferencia principal
usize es un entero sin signo cuyo tamaño depende de la arquitectura del sistema. En un sistema de 64 bits ocupa 8 bytes, en uno de 32 bits, 4 bytes. u32 siempre ocupa 4 bytes, independientemente de la plataforma.
Pero la diferencia no es solo el tamaño. Lo principal es la semántica:
usize— para tamaños, índices y cantidad de elementos en coleccionesu32— para datos numéricos que no superen los 4 mil millones
Y sí, el compilador no permite mezclarlos sin una conversión explícita. No es un error, es una característica: así Rust te protege de errores relacionados con el ancho de bits de la arquitectura.
¿Por qué existe usize?
Imagina que escribes código que debe funcionar tanto en un viejo microcontrolador de 32 bits como en un servidor moderno de 64 bits. La longitud de un vector en un sistema de 32 bits no puede superar 2^32 - 1 elementos, mientras que en uno de 64 bits es 2^64 - 1.
Si usaras u32 para indexar en un sistema de 64 bits, simplemente no podrías acceder a elementos más allá de los 4 mil millones, aunque hubiera memoria suficiente. usize resuelve este problema: siempre puede contener el tamaño máximo de memoria posible en la arquitectura actual.
// Ejemplo: usize se adapta automáticamente a la arquitecturafn main() { let large_vec: Vec<u8> = vec![0; 1_000_000_000]; // 1 mil millones de elementos // usize puede contener este índice en cualquier plataforma let index: usize = 999_999_999; println!("Elemento: {}", large_vec[index]); // En cambio, u32 podría no ser suficiente en un sistema de 32 bits // let bad_index: u32 = 999_999_999; // En un sistema de 32 bits esto compilaría, pero... // large_vec[bad_index as usize]; // ...habría que hacer la conversión}Comparación en tabla
| Característica | usize | u32 |
|---|---|---|
| Tamaño en 64 bits | 8 bytes | 4 bytes |
| Tamaño en 32 bits | 4 bytes | 4 bytes |
| Rango (64 bits) | 0 .. 2^64-1 | 0 .. 2^32-1 |
| Propósito principal | Índices, tamaños, longitudes | Datos numéricos |
| Conversión implícita | No | No |
¿Cuándo usar usize?
Siempre para indexar colecciones. Esta regla prácticamente no tiene excepciones. Si escribes vec[i] donde i es un u32, el compilador te dirá: «Oye, amigo, mejor usa usize».
// Correcto: usamos usize para índicesfn get_element(vec: &[i32], index: usize) -> Option<&i32> { vec.get(index)}
// Incorrecto: u32 para indexar — habrá que convertirfn get_ele