Menu
Coddy logo textTech

Errores centinela

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 55 de 107.

Un error centinela es una variable de error predefinida a nivel de paquete que representa una condición de error específica. A diferencia de crear nuevos errores cada vez con errors.New, los errores centinela se declaran una vez y se reutilizan en todo el código.

Los errores centinela se definen como variables a nivel de paquete, normalmente con nombres que empiezan por Err:

package user

import "errors"

var (
    ErrNotFound     = errors.New("user not found")
    ErrInvalidEmail = errors.New("invalid email address")
    ErrDuplicate    = errors.New("user already exists")
)

Las funciones devuelven estos errores predefinidos cuando ocurren condiciones específicas:

func FindUser(id int) (*User, error) {
    user, exists := users[id]
    if !exists {
        return nil, ErrNotFound
    }
    return user, nil
}

El poder de los errores centinela es que quienes realizan las llamadas pueden verificar condiciones de error específicas comparando directamente los valores de error:

user, err := FindUser(42)
if err == ErrNotFound {
    fmt.Println("Creating new user...")
} else if err != nil {
    fmt.Println("Unexpected error:", err)
}

La biblioteca estándar de Go utiliza errores centinela extensamente. Por ejemplo, io.EOF señala el fin de la entrada, y sql.ErrNoRows indica un resultado de consulta vacío. Este patrón crea un contrato claro entre los paquetes y sus usuarios sobre qué errores esperar.

Sin embargo, las comprobaciones de igualdad simples fallan cuando los errores están envueltos. En la siguiente lección, aprenderás cómo errors.Is() resuelve este problema comprobando toda la cadena de errores.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de gestión de inventario que utiliza errores centinela (sentinel errors) para comunicar condiciones de fallo específicas. Definirás variables de error a nivel de paquete que los llamadores pueden verificar para manejar diferentes escenarios de manera adecuada.

Organizarás tu código en dos archivos:

  • inventory.go: Crea un sistema de inventario con errores centinela predefinidos para casos de fallo comunes.

    Define tres errores centinela a nivel de paquete:

    • ErrItemNotFound con el mensaje "item not found in inventory"
    • ErrOutOfStock con el mensaje "item is out of stock"
    • ErrInsufficientQuantity con el mensaje "insufficient quantity available"

    Crea un struct Inventory que contenga un mapa de nombres de artículos y sus cantidades. Implementa estos métodos:

    • NewInventory() *Inventory - crea un inventario vacío
    • AddItem(name string, quantity int) - añade o actualiza la cantidad de un artículo
    • GetQuantity(name string) (int, error) - devuelve la cantidad de un artículo, o ErrItemNotFound si no existe
    • Purchase(name string, quantity int) error - intenta comprar artículos. Devuelve ErrItemNotFound si el artículo no existe, ErrOutOfStock si la cantidad actual es cero, o ErrInsufficientQuantity si la cantidad solicitada excede el stock disponible. En caso de éxito, reduce el inventario y devuelve nil.
  • main.go: Lee las operaciones de inventario de la entrada y demuestra cómo los llamadores pueden verificar errores centinela específicos utilizando la comparación directa de igualdad.

    Lee el nombre del artículo y la cantidad a comprar de la entrada. Crea un inventario, añade el artículo "laptop" con una cantidad de 5, luego intenta la compra. Basándote en el error devuelto, imprime un mensaje específico que demuestre que identificaste qué error centinela ocurrió.

Se proporcionarán las siguientes entradas:

  • Línea 1: Nombre del artículo a comprar
  • Línea 2: Cantidad a comprar

Maneja el resultado verificando qué error específico fue devuelto:

  • Si err == ErrItemNotFound: imprime Error: Item '[name]' does not exist in our inventory
  • Si err == ErrOutOfStock: imprime Error: Item '[name]' is currently out of stock
  • Si err == ErrInsufficientQuantity: imprime Error: Cannot purchase [quantity] units of '[name]' - not enough in stock
  • Si tiene éxito: imprime Successfully purchased [quantity] units of '[name]'

Por ejemplo, dado laptop y 3, tu salida debería ser:

Successfully purchased 3 units of 'laptop'

Y dado phone y 2, tu salida debería ser:

Error: Item 'phone' does not exist in our inventory

Y dado laptop y 10, tu salida debería ser:

Error: Cannot purchase 10 units of 'laptop' - not enough in stock

Hoja de referencia

Un error centinela (sentinel error) es una variable de error predefinida a nivel de paquete que representa una condición de error específica. Los errores centinela se declaran una vez y se reutilizan en todo el código.

Define los errores centinela como variables a nivel de paquete, típicamente con nombres que comienzan con Err:

package user

import "errors"

var (
    ErrNotFound     = errors.New("user not found")
    ErrInvalidEmail = errors.New("invalid email address")
    ErrDuplicate    = errors.New("user already exists")
)

Las funciones devuelven estos errores predefinidos cuando ocurren condiciones específicas:

func FindUser(id int) (*User, error) {
    user, exists := users[id]
    if !exists {
        return nil, ErrNotFound
    }
    return user, nil
}

Quienes llaman a la función pueden verificar condiciones de error específicas comparando los valores de error directamente:

user, err := FindUser(42)
if err == ErrNotFound {
    fmt.Println("Creating new user...")
} else if err != nil {
    fmt.Println("Unexpected error:", err)
}

La biblioteca estándar de Go utiliza errores centinela de forma extensiva, como io.EOF y sql.ErrNoRows.

Pruébalo tú mismo

package main

import (
	"fmt"
)

func main() {
	// Leer entrada
	var itemName string
	var quantity int
	fmt.Scanln(&itemName)
	fmt.Scanln(&quantity)

	// Crear inventario y añadir "laptop" con cantidad 5
	inv := NewInventory()
	inv.AddItem("laptop", 5)

	// TODO: Intentar comprar el artículo
	// Llamar a inv.Purchase(itemName, quantity)

	// TODO: Comprobar qué error centinela específico se devolvió utilizando una comparación de igualdad directa
	// Usar sentencias if/else if para comprobar:
	// - err == ErrItemNotFound
	// - err == ErrOutOfStock
	// - err == ErrInsufficientQuantity
	// - err == nil (éxito)

	// TODO: Imprimir el mensaje apropiado basado en el error
	// Si ErrItemNotFound: fmt.Printf("Error: Item '%s' does not exist in our inventory\n", itemName)
	// Si ErrOutOfStock: fmt.Printf("Error: Item '%s' is currently out of stock\n", itemName)
	// Si ErrInsufficientQuantity: fmt.Printf("Error: Cannot purchase %d units of '%s' - not enough in stock\n", quantity, itemName)
	// Si tiene éxito: fmt.Printf("Successfully purchased %d units of '%s'\n", quantity, itemName)
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos