Envolver errores

Una alternativa a los errores en Boxes es envolverlos en su propio tipo de error.

use std::error;
use std::error::Error as _;
use std::num::ParseIntError;
use std::fmt;

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

#[derive(Debug)]
enum ErrorAlDuplicar {
    VecVacio,
    // Deferiremos a la implementación del error de análisis por su error.
    // Proporcionar información adicional requiere agregar más datos al tipo.
    Parse(ParseIntError),
}

impl fmt::Display for ErrorAlDuplicar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ErrorAlDuplicar::VecVacio =>
                write!(f, "por favor usa un vector con al menos un elemento"),
            // El error envuelto contiene información adicional y está disponible a
            // través del método source().
            ErrorAlDuplicar::Parse(..) =>
                write!(f, "la cadena proporcionada no se pudo analizar como int"),
        }
    }
}

impl error::Error for ErrorAlDuplicar {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match *self {
            ErrorAlDuplicar::VecVacio => None,
            // La causa es el tipo de error de implementación subyacente. Se lanza
            // implícitamente al objeto de rasgo `&error::Error`. Esto funciona porque
            // el tipo subyacente ya implementa el rasgo `Error`.
            ErrorAlDuplicar::Parse(ref e) => Some(e),
        }
    }
}

// Implementa la conversión de `ParseIntError` a` ErrorAlDuplicar`.
// Esta será llamada automáticamente por `?` si un `ParseIntError` necesita ser
// convertido en un `ErrorAlDuplicar`.
impl From<ParseIntError> for ErrorAlDuplicar {
    fn from(err: ParseIntError) -> ErrorAlDuplicar {
        ErrorAlDuplicar::Parse(err)
    }
}

fn duplica_primer_elemento(vec: Vec<&str>) -> Result<i32> {
    let first = vec.first().ok_or(ErrorAlDuplicar::VecVacio)?;
    // Aquí usamos implícitamente la implementación `ParseIntError` de `From` (que
    // definimos anteriormente) para crear un `ErrorAlDuplicar`.
    let parsed = first.parse::<i32>()?;

    Ok(2 * parsed)
}

fn imprime(result: Result<i32>) {
    match result {
        Ok(n)  => println!("El doble del primer elemento es {}", n),
        Err(e) => {
            println!("Error: {}", e);
            if let Some(source) = e.source() {
                println!("  Causado por: {}", source);
            }
        },
    }
}

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));
}

Esto agrega un poco más de repetición para manejar errores y puede que no sea necesario en todas las aplicaciones. Hay algunas bibliotecas que pueden encargarse del texto estándar por ti.

Ve también

From::from y Enums