En Rust, la conversión entre tipos se divide en tres categorías principales según su seguridad y semántica: casting primitivo, conversiones garantizadas y conversiones fallibles. Entender cuándo usar cada una es crucial para evitar errores de lógica y pérdida de datos silenciosa.
El operador as se utiliza para el casting de tipos primitivos. Es una operación rápida que no verifica si la conversión es segura. En tipos enteros, si conviertes un tipo grande a uno pequeño (por ejemplo, de i32 a i8), el valor se truncará, manteniendo solo los bits significativos del tipo destino. En tipos de punto flotante, as puede producir NaN (Not a Number) o valores de infinito. El mayor riesgo de as es que no devuelve un error; si la conversión resulta en un valor inesperado, el programa continuará con datos erróneos.
Para conversiones que siempre tienen éxito y son semánticamente seguras, se utilizan los traits From e Into. El trait From<T> permite definir cómo convertir un tipo T en otro. Una vez que implementas From, el trait Into se implementa automáticamente para el tipo de destino. Estas conversiones son ideales cuando el tipo de destino puede contener todos los valores posibles del tipo de origen sin pérdida de información (como de u32 a u64). Además, existe la identidad: From<T> for T siempre está implementado, ya que convertir un tipo en sí mismo no requiere transformación.
Cuando la conversión es potencialmente insegura (como pasar un i32 con valor 300 a un i8), debemos usar TryFrom o TryInto. Estos métodos devuelven un Result<T, T::Error>, obligando al programador a manejar el caso en que el valor no quepa en el tipo de destino. Por último, para convertir cadenas de texto (&str o String) en tipos numéricos o estructuras, se utiliza el trait FromStr mediante el método .parse().
fn main() {
// 1. Casting con 'as' (Potencialmente inseguro/truncamiento)
let valor_grande: i32 = 300;
// El valor 300 en binario no cabe en i8, se produce un truncamiento
let valor_truncado = valor_grande as i8;
println!("Casting 'as' (300 as i8): {}", valor_truncado);
// 2. Conversión segura con 'From' e 'Into' (Sin pérdida de datos)
let num_u32: u32 = 100;
// 'From' es usado para la conversión directa
let num_u64_from = u64::from(num_u32);
// 'Into' es la forma invertida y permite encadenar métodos
let num_u64_into: u64 = num_u32.into();
println!("From: {}, Into: {}", num_u64_from, num_u64_into);
// 3. Conversión fallible con 'TryFrom' y 'TryInto'
let numero_i32: i32 = 300;
// Intentamos convertir un i32 que excede el rango de i8
let resultado_try = i8::try_from(numero_i32);
match resultado_try {
Ok(valor_exitoso) => println!("TryFrom exitoso: {}", valor_exitoso),
Err(e) => println!("TryFrom falló (error de rango): {}", e),
}
// 4. Parseo de strings con 'FromStr' (vía .parse())
let texto = "42";
// .parse() utiliza la implementación de FromStr para el tipo especificado
let numero_parseado = texto.parse::<i32>().expect("Error al parsear");
println!("Parseo de string: {}", numero_parseado);
}
Explicación del Código
valor_grande as i8: Aquí se aplica el operadoras. Como300supera el límite máximo de uni8(127), el compilador trunca los bits, resultando en un valor distinto al original sin lanzar error.u64::from(num_u32): Utiliza el traitFrom. Es seguro porque unu64puede representar cualquier valor que unu32contenga, garantizando que no haya pérdida de información.num_u32.into(): Es la versión deInto. Funciona exactamente igual queFrom, pero se llama desde la instancia del objeto.i8::try_from(numero_i32): ImplementaTryFrom. Al intentar meter300en uni8, el método devuelve unErrconteniendo la información del desbordamiento, permitiendo un manejo controlado mediantematch.texto.parse::<i32>(): Este método invoca internamentei32::from_str. El uso de la turbofish::<i32>le indica al compilador qué tipo queremos obtener del string.
N° 122