Skip to content

Commit 69e980e

Browse files
committed
feat: add custom error
1 parent ae96fbe commit 69e980e

File tree

4 files changed

+362
-141
lines changed

4 files changed

+362
-141
lines changed

content/3.error-handling/2.result.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ data:
1313
- name: 'Libro Oficial'
1414
english: false
1515
link: 'https://book.rustlang-es.org/ch06-01-defining-an-enum'
16-
- name: 'Documentacion Oficial'
17-
english: false
18-
link: 'https://google.github.io/comprehensive-rust/es/std-types/option.html'
1916
- name: 'Comprehensive Rust'
17+
english: false
18+
link: 'https://google.github.io/comprehensive-rust/es/std-types/result.html'
19+
- name: 'Documentacion Oficial'
2020
english: true
21-
link: 'https://doc.rust-lang.org/std/option'
21+
link: 'https://doc.rust-lang.org/stable/std/result'
2222
- name: '¿Cómo almacena Rust los enum en memoria?'
2323
english: false
2424
link: 'https://blog.rustlang-es.org/articles/como-almacena-rust-los-enum-en-memoria'

content/3.error-handling/3.operator.md

Lines changed: 0 additions & 137 deletions
This file was deleted.
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
---
2+
title: 'Operador de Propagación (`?`)'
3+
description: 'Mejorando el manejo de errores con el Operador de Propagación (`?`)'
4+
draft: true
5+
data:
6+
type: 'custom'
7+
topicLevel: 'start'
8+
position:
9+
x: 255
10+
y: 520
11+
width: 320
12+
externalLinks:
13+
- name: 'Libro Oficial'
14+
english: false
15+
link: 'https://book.rustlang-es.org/ch06-01-defining-an-enum'
16+
- name: 'Documentacion Oficial'
17+
english: false
18+
link: 'https://google.github.io/comprehensive-rust/es/std-types/option.html'
19+
- name: 'Comprehensive Rust'
20+
english: true
21+
link: 'https://doc.rust-lang.org/std/option'
22+
- name: '¿Cómo almacena Rust los enum en memoria?'
23+
english: false
24+
link: 'https://blog.rustlang-es.org/articles/como-almacena-rust-los-enum-en-memoria'
25+
---
26+
## Creando Tus Propios Errores en Rust: Buenas Prácticas y Herramientas
27+
28+
El manejo de errores en Rust es fundamental para construir aplicaciones seguras y eficientes. Aunque `Result<T, E>` y `Option<T>` cubren la mayoría de los casos, cuando desarrollas bibliotecas o aplicaciones más complejas, es común que necesites crear tus propios errores personalizados. En este post, te guiaré por las mejores prácticas para crear tus propios errores en Rust, destacando el uso de enumeradores, las limitaciones de `Box<dyn Error>`, y cómo mejorar tu código con crates como `thiserror`, `anyhow` y `eyre`.
29+
30+
### ¿Por qué crear tus propios errores?
31+
32+
Rust fomenta el manejo explícito de errores mediante el uso de `Result` y `Option`. En muchos casos, el error que quieres propagar no está cubierto por los tipos estándar como `std::io::Error` o `std::fmt::Error`, por lo que crear tus propios tipos de error te permite:
33+
34+
1. **Especificar el contexto exacto del fallo**.
35+
2. **Incluir variantes para distintos tipos de errores**.
36+
3. **Proporcionar mejores mensajes de error**.
37+
4. **Asegurarte de que el manejo de errores sea parte de la lógica del flujo de tu aplicación**.
38+
39+
### Creando Errores con `enum`
40+
41+
El enfoque recomendado para crear errores personalizados en Rust es usar un `enum`. Un `enum` puede representar diferentes variantes de error que pueden ocurrir en tu programa o biblioteca. Esto te permite manejar distintos tipos de errores de forma estructurada, con la ventaja de que el compilador te obliga a tratar todos los casos.
42+
43+
Aquí tienes un ejemplo básico:
44+
45+
```rust
46+
use std::fmt;
47+
48+
#[derive(Debug)]
49+
enum MyError {
50+
NotFound,
51+
InvalidInput(String),
52+
IoError(std::io::Error),
53+
}
54+
55+
impl fmt::Display for MyError {
56+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57+
match self {
58+
MyError::NotFound => write!(f, "Recurso no encontrado"),
59+
MyError::InvalidInput(msg) => write!(f, "Entrada inválida: {}", msg),
60+
MyError::IoError(e) => write!(f, "Error de IO: {}", e),
61+
}
62+
}
63+
}
64+
65+
impl std::error::Error for MyError {}
66+
67+
fn main() -> Result<(), MyError> {
68+
// Simulamos un error
69+
Err(MyError::NotFound)
70+
}
71+
```
72+
73+
En este ejemplo:
74+
75+
- `MyError` puede representar diferentes errores (`NotFound`, `InvalidInput` y `IoError`).
76+
- Implementamos `Display` para personalizar el mensaje de error y `std::error::Error` para integrarnos con el sistema de errores de Rust.
77+
78+
### Por Qué Evitar `Box<dyn Error>`
79+
80+
Una alternativa común es usar `Box<dyn std::error::Error>` para empaquetar errores. Esto permite la creación de errores dinámicos en tiempo de ejecución. Sin embargo, **esto no es recomendado**, ya que sacrifica el control estático que proporciona el sistema de tipos. Además, el uso de `Box<dyn Error>` tiene un coste en rendimiento, ya que introduce asignaciones en el heap y elimina la capacidad de optimización en tiempo de compilación.
81+
82+
Por ejemplo:
83+
84+
```rust
85+
fn error_con_box() -> Result<(), Box<dyn std::error::Error>> {
86+
let io_error = std::fs::File::open("archivo_inexistente.txt")?;
87+
Ok(())
88+
}
89+
```
90+
91+
Aunque `Box<dyn Error>` es flexible, oculta la verdadera naturaleza de los errores que pueden ocurrir, lo que complica el manejo en el código llamador.
92+
93+
### Mejorando el Manejo de Errores con `thiserror`
94+
95+
Para evitar el tedio de implementar manualmente `Display`, `From`, y otras interfaces para tus errores, puedes usar el crate [`thiserror`](https://docs.rs/thiserror/latest/thiserror/). `thiserror` simplifica la creación de errores personalizados al generar automáticamente el código boilerplate, manteniendo tu código más limpio.
96+
97+
Aquí tienes el mismo ejemplo anterior usando `thiserror`:
98+
99+
```rust
100+
use thiserror::Error;
101+
102+
#[derive(Error, Debug)]
103+
enum MyError {
104+
#[error("Recurso no encontrado")]
105+
NotFound,
106+
107+
#[error("Entrada inválida: {0}")]
108+
InvalidInput(String),
109+
110+
#[error(transparent)]
111+
IoError(#[from] std::io::Error),
112+
}
113+
114+
fn main() -> Result<(), MyError> {
115+
// Simulamos un error
116+
Err(MyError::NotFound)
117+
}
118+
```
119+
120+
### Ventajas de `thiserror`:
121+
122+
1. **Menos boilerplate:** No necesitas escribir manualmente implementaciones de `Display` o `From`.
123+
2. **Uso de atributos:** Puedes usar atributos para personalizar los mensajes de error y para manejar automáticamente la conversión de errores (como `#[from]`).
124+
3. **Mejor mantenimiento:** Al eliminar el código repetitivo, tu código se vuelve más legible y fácil de mantener.
125+
126+
### Uso de `anyhow` y `eyre` para Errores Genéricos
127+
128+
Si necesitas manejar errores de manera más flexible y no te importa tanto el tipo específico del error (por ejemplo, en aplicaciones CLI o proyectos rápidos), puedes usar el crate [`anyhow`](https://docs.rs/anyhow/latest/anyhow/) o su contraparte personalizable [`eyre`](https://docs.rs/color-eyre/latest/color-eyre/). Estos crates proporcionan tipos de error genéricos y facilitan el manejo de errores de una manera más sencilla, pero sin perder información útil.
129+
130+
#### Ejemplo con `anyhow`:
131+
132+
```rust
133+
use anyhow::{Result, Context};
134+
135+
fn funcion_con_error() -> Result<()> {
136+
let _file = std::fs::File::open("archivo.txt")
137+
.with_context(|| "No se pudo abrir el archivo")?;
138+
Ok(())
139+
}
140+
141+
fn main() -> Result<()> {
142+
funcion_con_error()?;
143+
Ok(())
144+
}
145+
```
146+
147+
En este ejemplo:
148+
149+
- `anyhow::Result` es un alias conveniente para `Result<T, anyhow::Error>`.
150+
- `with_context()` proporciona mensajes de error adicionales, lo que facilita el diagnóstico de fallos.
151+
152+
#### Ejemplo con `eyre` y Errores Coloreados
153+
154+
`eyre` permite un manejo de errores similar a `anyhow`, pero con la capacidad de agregar "reportes" de errores enriquecidos y con colores, ideales para la salida de errores en la terminal:
155+
156+
```rust
157+
use color_eyre::eyre::Result;
158+
159+
fn main() -> Result<()> {
160+
color_eyre::install()?; // Habilita los reportes coloreados
161+
162+
let _file = std::fs::File::open("archivo.txt")?;
163+
Ok(())
164+
}
165+
```
166+
167+
### Comparativa de Opciones
168+
169+
| **Método** | **Ventajas** | **Desventajas** |
170+
|----------------------------|------------------------------------------------|--------------------------------------------|
171+
| **Enum manual** | Control preciso, tipado estático | Mucho código repetitivo |
172+
| **`Box<dyn Error>`** | Flexibilidad para manejar cualquier error | Oculta la naturaleza del error, más lento |
173+
| **`thiserror`** | Fácil de usar, sin boilerplate, mantiene tipos | Menos flexible que `Box<dyn Error>` |
174+
| **`anyhow`** | Simplicidad en el manejo de errores genéricos | Pierdes detalles sobre el tipo de error |
175+
| **`eyre`** | Reportes detallados y coloreados | Similar a `anyhow` en términos de flexibilidad |
176+
177+
### Conclusión
178+
179+
Crear tus propios errores en Rust es una parte crucial de escribir código seguro y robusto. Usar `enum` te proporciona el máximo control sobre el manejo de errores, mientras que herramientas como `thiserror` y `anyhow` te permiten reducir el código repetitivo y mejorar la legibilidad. Dependiendo del contexto de tu aplicación, puedes elegir entre un enfoque más tipado o uno más flexible. En cualquier caso, Rust proporciona las herramientas necesarias para garantizar que los errores sean manejados de manera segura y eficiente.

0 commit comments

Comments
 (0)