El operador ? es un “azúcar sintáctico” diseñado para simplificar el manejo de errores, evitando la necesidad de escribir repetitivos bloques match para cada operación que pueda fallar. Su función principal es la propagación: si una expresión devuelve un error, el operador detiene la ejecución de la función actual y devuelve ese error inmediatamente al llamador.
Este operador funciona de manera distinta según el tipo de retorno de la expresión:
1. En tipos Result<T, E>: Si el resultado es Ok(v), el operador extrae el valor v y permite que la ejecución continúe. Si es Err(e), la función actual retorna prematuramente Err(e.into()). Este into() es crucial, ya que utiliza la convención From para convertir automáticamente el tipo de error actual (e) al tipo de error definido en la firma de la función receptora.
2. En tipos Option<T>: Si el valor es Some(v), se extrae v. Si es None, la función retorna None de inmediato.
El uso de ? es extremadamente potente para el encadenamiento de operaciones. Por ejemplo, se puede leer un archivo, procesar su contenido y convertirlo a un número en una sola línea de flujo lógico, donde cada paso que falle interrumpirá la cadena y propagará el error correspondiente. Sin embargo, existe una restricción fundamental: el operador ? solo puede utilizarse en funciones cuyo tipo de retorno sea compatible con Result o Option. Intentar usarlo en una función que devuelve un tipo simple (como i32 o String) resultará en un error de compilación.
use std::io::{self, ErrorKind};
/// Función que simula el procesamiento de una cadena de texto.
/// Retorna un Result que puede contener un error genérico (Box<dyn Error>).
fn procesar_entrada(entrada: &str) -> Result<i32, Box<dyn std::error::Error>> {
// Simulamos una validación que puede fallar (como un archivo vacío)
// Si la entrada está vacía, retornamos un error de I/O manualmente.
if entrada.is_empty() {
return Err(Box::new(io::Error::new(ErrorKind::InvalidData, "Entrada está vacía")));
}
// El operador ? extrae el valor si es Ok, o retorna el error si es Err.
// Aquí, parse() devuelve ParseIntError, pero el '?' lo convierte
// automáticamente a Box<dyn Error> gracias a la implementación de From.
let numero: i32 = entrada.trim().parse()?;
// Si todo sale bien, envolvemos el resultado en un Ok.
Ok(numero)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let datos_validos = " 42 ";
let datos_invalidos = "no_es_un_numero";
// Caso 1: Uso exitoso. El valor se extrae y se imprime.
let n1 = procesar_entrada(datos_validos)?;
println!("Número procesado con éxito: {}", n1);
// Caso 2: Propagación de error.
// El operador '?' en main permite que el error de procesar_entrada
// se propague hacia el entorno de ejecución, cerrando el programa con error.
println!("Intentando procesar dato inválido...");
let _n2 = procesar_entrada(datos_invalidos)?;
Ok(())
}
Análisis del Código
fn procesar_entrada(entrada: &str) -> Result<i32, Box<dyn std::error::Error>>: La firma de la función indica que puede devolver un entero o cualquier tipo de error que implemente el traitErrormediante el uso de un puntero de función dinámico (Box<dyn Error>). Esto es vital para que el operador?pueda convertir diferentes tipos de errores en uno solo.if entrada.is_empty() { return Err(...) }: Aquí se realiza una validación manual. Si se cumple la condición, se retorna un error de tipostd::io::Error.let numero: i32 = entrada.trim().parse()?;: Esta es la línea clave. El métodoparse()devuelve unResult<i32, ParseIntError>. El operador?detecta que el tipo de retorno deprocesar_entradaesBox<dyn Error>y llama automáticamente aFrom::from(error_de_parse)para convertir elParseIntErroren unBox<dyn Error>. Si la operación fuera exitosa,numerocontendría el valori32extraído.fn main() -> Result<(), Box<dyn std::error::Error>>: Es una práctica común en aplicaciones Rust para permitir el uso de?desde el punto de entrada principal. Simainretorna unErr, Rust imprimirá el error de forma descriptiva y finalizará con un código de salida de error.let n1 = procesar_entrada(datos_validos)?;: En el flujo normal, el?extrae el valor42y lo asigna an1.let _n2 = procesar_entrada(datos_invalidos)?;: Al llamar aprocesar_entradacon un texto no numérico,parse()devuelve unErr. El operador?intercepta este error y lo devuelve inmediatamente desdemain, evitando que la línea deprintln!siguiente se ejecute.
N° 59