Conversión de integer a string en Rust: magia sin unwrap ni macros

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é en Rust no se puede simplemente sumar un número con una cadena?

\n\n

Si vienes de Python o JavaScript, estás acostumbrado a escribir algo como str(42) o 42 + "". En Rust, ese truco no funciona. El compilador preguntará estrictamente: "¿Qué tipo quieres obtener? ¿Integer? ¿String? ¡Decídete!"

\n\n

La pregunta "How do I convert from an integer to a string?" es una de las más populares en Stack Overflow en la sección de Rust. Miles de desarrolladores se rompen la cabeza preguntándose por qué su código no compila cuando intentan escribir:

\n\n
let number = 42;\nlet text = number; // Error: expected `&str`, found `i32`
\n\n

Hoy exploraremos a fondo esta conversión. No solo aprenderás cómo hacerlo, sino también cómo funciona a nivel de traits, asignaciones de memoria e incluso ensamblador. ¡Allá vamos!

\n\n

La forma más sencilla: el método to_string()

\n\n

En Rust, como en la mayoría de los lenguajes modernos, los tipos enteros tienen el método to_string(). Se ve engañosamente simple:

\n\n
fn main() {\n    let num: i32 = 255;\n    let s: String = num.to_string();\n    println!("{}", s); // Muestra: "255"\n}
\n\n

Esto funciona para todos los tipos enteros: i8, u32, i64, usize, etc.

\n\n

¿Qué sucede internamente?

\n\n

El método to_string() no es magia del compilador. Se implementa a través del trait Display. En la biblioteca estándar de Rust existe esta implementación:

\n\n
// Versión simplificada de la biblioteca estándar\nimpl<T: fmt::Display> ToString for T {\n    #[inline]\n    fn to_string(&self) -> String {\n        use fmt::Write;\n        let mut buf = String::new();\n        // Aquí se llama al método fmt() del trait Display\n        let _ = buf.write_fmt(format_args!("{}", self));\n        buf.shrink_to_fit(); // Optimización de memoria\n        buf\n    }\n}
\n\n

Punto clave: to_string() crea un nuevo String en el heap. Cada llamada implica una asignación de memoria. Para cientos de miles de conversiones, esto puede convertirse en un cuello de botella de rendimiento.

\n\n

Alternativa: la macro format!

\n\n

La segunda forma popular es la macro format!. Funciona como println!, pero en lugar de mostrar en consola, devuelve un String:

\n\n
fn main() {\n    let num = 1024;\n    let s = format!("{}", num);\n    println!("Longitud de la cadena: {}", s.len()); // 4\n}
\n\n

La diferencia entre to_string() y format! es mínima. to_string() internamente llama a format_args! y write_fmt. En esencia, es lo mismo. La elección es cuestión de estilo y legibilidad.

\n\n

¿Cuándo usar format!?

\n
    \n
  • Cuando necesitas insertar un número en medio de una cadena: format!("Número: {}", num)
  • \n
  • Cuando el formato es complejo (hexadecimal, con separadores): format!("{:#x}", 255) → "0xff"
  • \n
  • Cuando la expresividad del código es importante
  • \n
\n\n

Trait Display vs ToString: ¿cuál es más rápido?

\n\n

Si escribes una biblioteca o código de alto rendimiento, usa directamente el trait Display a través de write! en un buffer. Esto permite reutilizar la memoria ya asignada:

\n\n
use std::fmt::Write;\n\nfn main() {\n    let num = 42;\n    let mut buffer = String::with_capacity(20); // Asignamos memoria de antemano\n\n    // Escribimos el número en el buffer\n    write!(buffer, "{}", num).unwrap();\n    println!("{}", buffer); // "42"\n\n    // Limpiamos y reutilizamos\n    buffer.clear();\n    write!(buffer, "{}", 256).unwrap();\n    println!("{}", buffer); // "256"\n}
\n\n

Este enfoque proporciona hasta 2-3 veces más rendimiento en bucles donde se necesita convertir números a cadenas repetidamente, porque la asignación de memoria ocu

Blogs

Book Recommendations