Envolver errores con un tipo personalizado

RustRustBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, se demuestra el enfoque alternativo de envolver errores en un tipo de error personalizado. El ejemplo de código muestra cómo definir un alias de tipo Result que utiliza la enumeración DoubleError como la variante de error, que envuelve el ParseIntError de la biblioteca estándar. Al implementar los rasgos fmt::Display, error::Error y From, el tipo de error personalizado puede proporcionar información adicional y manejar errores subyacentes.

Nota: Si el laboratorio no especifica un nombre de archivo, puede usar cualquier nombre de archivo que desee. Por ejemplo, puede usar main.rs, compilar y ejecutarlo con rustc main.rs &&./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/AdvancedTopicsGroup(["Advanced Topics"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/DataTypesGroup -.-> rust/integer_types("Integer Types") rust/DataTypesGroup -.-> rust/string_type("String Type") rust/DataTypesGroup -.-> rust/type_casting("Type Conversion and Casting") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") rust/AdvancedTopicsGroup -.-> rust/traits("Traits") rust/AdvancedTopicsGroup -.-> rust/operator_overloading("Traits for Operator Overloading") subgraph Lab Skills rust/variable_declarations -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} rust/integer_types -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} rust/string_type -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} rust/type_casting -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} rust/function_syntax -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} rust/expressions_statements -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} rust/method_syntax -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} rust/traits -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} rust/operator_overloading -.-> lab-99250{{"Envolver errores con un tipo personalizado"}} end

Envolver errores

Una alternativa a empaquetar errores es envolverlos en un tipo de error propio.

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

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

#[derive(Debug)]
enum DoubleError {
    EmptyVec,
    // Dejaremos que la implementación de error de análisis se ocupe de su error.
    // Proporcionar información adicional requiere agregar más datos al tipo.
    Parse(ParseIntError),
}

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

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

// Implemente la conversión de `ParseIntError` a `DoubleError`.
// Esto se llamará automáticamente por `?` si se necesita convertir un `ParseIntError`
// en un `DoubleError`.
impl From<ParseIntError> for DoubleError {
    fn from(err: ParseIntError) -> DoubleError {
        DoubleError::Parse(err)
    }
}

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

    Ok(2 * parsed)
}

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

fn main() {
    let numbers = vec!["42", "93", "18"];
    let empty = vec![];
    let strings = vec!["tofu", "93", "18"];

    print(double_first(numbers));
    print(double_first(empty));
    print(double_first(strings));
}

Esto agrega un poco más de código repetitivo para manejar errores y puede no ser necesario en todas las aplicaciones. Hay algunas bibliotecas que pueden encargarse del código repetitivo por ti.

Resumen

¡Felicitaciones! Has completado el laboratorio de Envolver Errores. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.