Conversión segura de números en Rust: guía completa para principiantes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿por qué la conversión de números en Rust no es una broma?

\n

¿Alguna vez ha escrito código en Python y ha hecho tranquilamente int_var = 3.14 y luego float_var = int_var? En Python esto funciona porque los tipos son dinámicos. Pero en Rust, como un estricto relojero, cada engranaje debe tener su tamaño. Y si intenta insertar un u8 (0–255) en un u16 es una cosa, pero si es al revés, puede ocurrir una "silenciosa" pérdida de datos o un pánico.

\n

En Stack Overflow, la pregunta «How do I convert between numeric types safely and idiomatically?» ha acumulado miles de visitas. Y no es casualidad: los principiantes a menudo se pierden entre as, From y TryFrom. En este artículo, desglosaremos todas las formas de convertir números en Rust, explicaremos cuándo usar cada una y mostraremos ejemplos que podrá aplicar de inmediato.

\n\n

1. Conversión simple con as: rápido, pero peligroso

\n

La forma más simple es el operador as. Funciona, pero con trampas.

\n
let big: u32 = 300;\nlet small: u8 = big as u8; // ¡300 no cabe en u8!\nprintln!("{}", small); // Salida: 44 (300 mod 256)\n
\n

¿Qué sucedió? Rust simplemente tomó los 8 bits inferiores del número 300 (0b1_0010_1100) y descartó todo lo demás, dejando 0b0010_1100 = 44. Esto se llama truncamiento (truncation). Para u8 esto puede ser una sorpresa.

\n

as solo es seguro al expandir el tipo (por ejemplo, u8 → u16), cuando el valor cabe con certeza. Para reducir, es una bomba de tiempo.

\n

¿Cuándo usar as? Solo cuando esté absolutamente seguro de que el valor no excederá los límites del tipo destino. Por ejemplo, al trabajar con máscaras de bits o protocolos de bajo nivel.

\n\n

2. Forma idiomática: los traits From e Into

\n

Rust es un lenguaje donde los errores deben ser explícitos. Por eso, la biblioteca estándar proporciona los traits From e Into para una conversión segura que nunca falla.

\n

La regla es simple: si la conversión siempre tiene éxito (sin pérdida de datos), el trait está implementado. Por ejemplo:

\n
let small: u8 = 42;\nlet big: u16 = small.into(); // Into llama automáticamente a From\n// o\nlet big: u16 = u16::from(small);\nprintln!("{}", big); // 42\n
\n

Atención: From solo está implementado para pares donde la conversión está garantizada como segura. Por ejemplo, u8 → u16, i32 → f64, bool → i32. Pero u16 → u8 no, porque podría haber desbordamiento.

\n

Intente compilar este código:

\n
let big: u16 = 256;\nlet small: u8 = big.into(); // ¡Error de compilación!\n
\n

El compilador dirá: "the trait `From` is not implemented for `u8`". Esto es seguridad: no puede perder datos accidentalmente.

\n\n

3. Cuando la conversión puede fallar: TryFrom y TryInto

\n

¿Y si aún así queremos intentar convertir u16 a u8, pero con verificación? Aquí vienen al rescate TryFrom y TryInto. Devuelven un Result que debe ser manejado.

\n
use std::convert::TryFrom;\n\nlet big: u16 = 255;\nmatch u8::try_from(big) {\n    Ok(small) => println!("Éxito: {}", small),\n    Err(e) => println!("Error: {}", e),\n}\n// Salida: Éxito: 255\n\nlet big: u16 = 256;\nmatch u8::try_from(big) {\n    Ok(small) => println!("Éxito: {}", small),\n    Err(e) => println!("Error: {}", e),\n}\n// Salida: Error: TryFromIntError(())\n
\n

Esto es Rust idiomático: maneja explícitamente el posible error. Puede usar unwrap() si está 100% seguro, pero es mejor usar match.

Blogs

Book Recommendations