Definir un tipo de error

A veces, simplifica el código para enmascarar todos los diferentes errores con un solo tipo de error. Mostraremos esto con un error personalizado.

Rust nos permite definir nuestros propios tipos de error. En general, un tipo de error "bueno":

  • Representa diferentes errores con el mismo tipo
  • Presenta buenos mensajes de error al usuario
  • Es fácil de comparar con otros tipos.
    • Bueno: Err(EmptyVec)
    • Malo: Err("Por favor usa un vector con al menos un elemento".to_owned())
  • Puede contener información sobre el error
    • Bueno: Err(BadChar (c, posición))
    • Malo: Err("+ no se puede usar aquí".to_owned())
  • Se compone bien con otros errores.
use std::fmt;

type Result<T> = std::result::Result<T, ErrorAlDuplicar>;

// Define nuestros tipos de error. Estos pueden personalizarse para nuestros casos de
// manejo de errores.
// Ahora podremos escribir nuestros propios errores, diferir a un error de
// implementación subyacente, o hacer algo intermedio.
#[derive(Debug, Clone)]
struct ErrorAlDuplicar;

// La generación de un error es completamente independiente de cómo se muestra.
// No hay necesidad de preocuparse por saturar la lógica compleja con el estilo de
// visualización.

// Ten en cuenta que no almacenamos información adicional sobre los errores. Esto
// significa que no podemos indicar qué cadena no se pudo analizar sin modificar
// nuestros tipos para que lleven esa información.
// No hay necesidad de preocuparse por saturar la lógica compleja con el estilo de
// visualización.
impl fmt::Display for ErrorAlDuplicar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "primer elemento inválido para duplicar")
    }
}

fn duplica_primer_elemento(vec: Vec<&str>) -> Result<i32> {
    vec.first()
        // Cambia el error a nuestro nuevo tipo.
        .ok_or(ErrorAlDuplicar)
        .and_then(|s| {
            s.parse::<i32>()
                // Actualiza al nuevo tipo de error aquí también.
                .map_err(|_| ErrorAlDuplicar)
                .map(|i| 2 * i)
        })
}

fn imprime(result: Result<i32>) {
    match result {
        Ok(n) => println!("El doble del primer elemento es {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    let numeros = vec!["42", "93", "18"];
    let vacio = vec![];
    let cadenas = vec!["tofu", "93", "18"];

    imprime(duplica_primer_elemento(numeros));
    imprime(duplica_primer_elemento(vacio));
    imprime(duplica_primer_elemento(cadenas));
}