Manejo de Errores
## Objetivos de Aprendizaje
- Comprender la filosofía de manejo de errores de Go
- Trabajar con la interface error
- Crear errores personalizados
- Manejar errores efectivamente
- Usar panic y recover apropiadamente
## Filosofía de Errores de Go
Go usa retornos de error explícitos en lugar de excepciones.
```go
result, err := riskyOperation()
if err != nil {
// Manejar error
}
```
## La Interface error
```go
type error interface {
Error() string
}
```
### Error Básico
```go
import "errors"
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("división por cero")
}
return a / b, nil
}
```
## Creando Errores
### errors.New
```go
import "errors"
err := errors.New("algo salió mal")
fmt.Println(err) // algo salió mal
```
### fmt.Errorf
```go
import "fmt"
err := fmt.Errorf("entrada inválida: %s", "valor faltante")
```
### Tipos de Error Personalizados
```go
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func validate(name string) error {
if name == "" {
return &ValidationError{Field: "name", Message: "no puede estar vacío"}
}
return nil
}
```
### Envoltura de Errores (Go 1.13+)
```go
import "fmt"
func readConfig(path string) error {
err := os.Open(path)
if err != nil {
return fmt.Errorf("falló al abrir config: %w", err)
}
return nil
}
```
### Unwrap
```go
import "errors"
err := readConfig("missing.txt")
if err != nil {
// Verificar error original
if os.IsNotExist(err) {
fmt.Println("El archivo no existe")
}
// Cadena de unwrap
for err != nil {
fmt.Println(err)
err = errors.Unwrap(err)
}
}
```
## Manejando Errores
### Patrón Básico
```go
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Resultado:", result)
```
### Retorno Anticipado
```go
func process(data string) error {
file, err := os.Open(data)
if err != nil {
return err // Retorno anticipado en error
}
defer file.Close()
// Continuar procesamiento...
return nil
}
```
### Múltiples Errores
```go
func processAll(files []string) error {
for _, file := range files {
if err := process(file); err != nil {
return fmt.Errorf("procesando %s: %w", file, err)
}
}
return nil
}
```
## Patrones de Verificación de Errores
### Errores Centinela
```go
import "errors"
import "os"
var ErrNotFound = errors.New("recurso no encontrado")
func find(id string) (string, error) {
if id == "missing" {
return "", ErrNotFound
}
return "encontrado", nil
}
if err := find("missing"); err != nil {
if errors.Is(err, ErrNotFound) {
fmt.Println("Recurso no encontrado")
}
}
```
### errors.Is
```go
import "errors"
err := find("missing")
if errors.Is(err, ErrNotFound) {
// Manejar no encontrado
}
```
### errors.As
```go
type CustomError struct {
Code int
Msg string
}
func (e *CustomError) Error() string {
return e.Msg
}
var cerr *CustomError
if errors.As(err, &cerr) {
fmt.Printf("Código de error personalizado: %d\n", cerr.Code)
}
```
## Funciones Comunes de Error
### Errores de os
```go
import "os"
file, err := os.Open("missing.txt")
if err != nil {
os.IsNotExist(err) // true si el archivo no existe
os.IsPermission(err) // true si permiso denegado
os.IsExist(err) // true si el archivo ya existe
}
```
### Errores de net
```go
import "net"
_, err := net.Dial("tcp", "localhost:9999")
if err != nil {
var opErr *net.OpError
if errors.As(err, &opErr) {
fmt.Println("Operación:", opErr.Op)
}
}
```
## Manejo de Errores Personalizado
### Manejador de Error
```go
type Handler func(error)
func handle(h Handler, err error) {
if err != nil {
h(err)
}
}
```
### Patrón de Reintento
```go
func withRetry(maxRetries int, fn func() error) error {
var err error
for i := 0; i < maxRetries; i++ {
err = fn()
if err == nil {
return nil
}
time.Sleep(time.Duration(i+1) * time.Second)
}
return fmt.Errorf("después de %d reintentos: %w", maxRetries, err)
}
```
## panic y recover
### Cuándo Usar panic
- Reservado para situaciones verdaderamente irrecuperables
- Errores de programación (puntero nil, fuera de límites)
- No para condiciones de error esperadas
### panic
```go
func main() {
panic("¡algo malo pasó!")
fmt.Println("Esto no se ejecutará")
}
```
### recover
```go
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recuperado de:", r)
}
}()
panic("¡Algo salió mal!")
fmt.Println("Esto no se ejecutará")
}
func main() {
safeCall()
fmt.Println("El programa continúa...")
}
```
### Recover en Defer
```go
func mayPanic() {
panic("¡panic!")
}
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Capturado:", r)
}
}()
mayPanic()
fmt.Println("Después de mayPanic")
}
```
### Mejor Práctica: Recuperación en el Borde
```go
func server() {
defer func() {
if r := recover(); r != nil {
log.Printf("Server panic: %v", r)
os.Exit(1)
}
}()
// Lógica del servidor...
}
```
## Registrar Errores
### Paquete log
```go
import "log"
if err != nil {
log.Printf("operación fallida: %v", err)
}
if err != nil {
log.Fatalf("error crítico: %v", err)
}
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
```
### Logger Personalizado
```go
logger := log.New(os.Stderr, "APP: ", log.Ldate|log.Ltime)
logger.Printf("Iniciando servidor en puerto %d", 8080)
logger.Fatalf("Falló al iniciar: %v", err)
```
## Visibilidad de Errores
### Evitar Errores Ocultos
```go
// MALO: ignora error
bytes, _ := json.Marshal(data)
// BUENO: manejar error
bytes, err := json.Marshal(data)
if err != nil {
return fmt.Errorf("marshaling data: %w", err)
}
```
### Sin Supresión de Errores
```go
// MALO: silenciosamente ignora errores
if x > 0 {
fmt.Println("positivo")
} else if x < 0 {
fmt.Println("negativo")
}
// ¡caso else ignorado!
// BUENO
if x > 0 {
fmt.Println("positivo")
} else if x < 0 {
fmt.Println("negativo")
} else {
fmt.Println("cero")
}
```
## Resumen
- Go usa manejo de errores explícito mediante valores de retorno
- La interface `error` tiene un método: `Error() string`
- Crear errores con `errors.New()` o `fmt.Errorf()`
- Usar verbo `%w` para envolver errores
- `errors.Is()` y `errors.As()` para inspección de errores
- panic/recover reservado para situaciones verdaderamente excepcionales
- Registrar o manejar cada error explícitamente
- Nunca suprimir errores silenciosamente
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →