Go
Manejo de errores
Buscar..
Introducción
En Go, las situaciones inesperadas se manejan utilizando errores , no excepciones. Este enfoque es más similar al de C, usando errno, que al de Java u otros lenguajes orientados a objetos, con sus bloques try / catch. Sin embargo, un error no es un entero sino una interfaz.
Una función que puede fallar normalmente devuelve un error como último valor de retorno. Si este error no es nulo , algo salió mal y la persona que llama a la función debería actuar en consecuencia.
Observaciones
Observe cómo en Go no genera un error. En su lugar, devuelve un error en caso de fallo.
Si una función puede fallar, el último valor devuelto es generalmente un tipo de error
.
// This method doesn't fail
func DoSomethingSafe() {
}
// This method can fail
func DoSomething() (error) {
}
// This method can fail and, when it succeeds,
// it returns a string.
func DoAndReturnSomething() (string, error) {
}
Creando un valor de error
La forma más sencilla de crear un error es mediante el uso del paquete de errors
.
errors.New("this is an error")
Si desea agregar información adicional a un error, el paquete fmt
también proporciona un método útil de creación de errores:
var f float64
fmt.Errorf("error with some additional information: %g", f)
Aquí hay un ejemplo completo, donde el error es devuelto desde una función:
package main
import (
"errors"
"fmt"
)
var ErrThreeNotFound = errors.New("error 3 is not found")
func main() {
fmt.Println(DoSomething(1)) // succeeds! returns nil
fmt.Println(DoSomething(2)) // returns a specific error message
fmt.Println(DoSomething(3)) // returns an error variable
fmt.Println(DoSomething(4)) // returns a simple error message
}
func DoSomething(someID int) error {
switch someID {
case 3:
return ErrThreeNotFound
case 2:
return fmt.Errorf("this is an error with extra info: %d", someID)
case 1:
return nil
}
return errors.New("this is an error")
}
Creando un tipo de error personalizado
En Go, un error está representado por cualquier valor que pueda describirse como una cadena. Cualquier tipo que implemente la interfaz de error
incorporada es un error.
// The error interface is represented by a single
// Error() method, that returns a string representation of the error
type error interface {
Error() string
}
El siguiente ejemplo muestra cómo definir un nuevo tipo de error usando un literal compuesto de cadena.
// Define AuthorizationError as composite literal
type AuthorizationError string
// Implement the error interface
// In this case, I simply return the underlying string
func (e AuthorizationError) Error() string {
return string(e)
}
Ahora puedo usar mi tipo de error personalizado como error:
package main
import (
"fmt"
)
// Define AuthorizationError as composite literal
type AuthorizationError string
// Implement the error interface
// In this case, I simply return the underlying string
func (e AuthorizationError) Error() string {
return string(e)
}
func main() {
fmt.Println(DoSomething(1)) // succeeds! returns nil
fmt.Println(DoSomething(2)) // returns an error message
}
func DoSomething(someID int) error {
if someID != 1 {
return AuthorizationError("Action not allowed!")
}
// do something here
// return a nil error if the execution succeeded
return nil
}
Devolviendo un error
En Go no generas un error. En su lugar, devuelve un error
en caso de fallo.
// This method can fail
func DoSomething() error {
// functionThatReportsOK is a side-effecting function that reports its
// state as a boolean. NOTE: this is not a good practice, so this example
// turns the boolean value into an error. Normally, you'd rewrite this
// function if it is under your control.
if ok := functionThatReportsOK(); !ok {
return errors.New("functionThatReportsSuccess returned a non-ok state")
}
// The method succeeded. You still have to return an error
// to properly obey to the method signature.
// But in this case you return a nil error.
return nil
}
Si el método devuelve varios valores (y la ejecución puede fallar), entonces la convención estándar es devolver el error como el último argumento.
// This method can fail and, when it succeeds,
// it returns a string.
func DoAndReturnSomething() (string, error) {
if os.Getenv("ERROR") == "1" {
return "", errors.New("The method failed")
}
s := "Success!"
// The method succeeded.
return s, nil
}
result, err := DoAndReturnSomething()
if err != nil {
panic(err)
}
Manejando un error
En Go los errores pueden devolverse desde una llamada de función. La convención es que si un método puede fallar, el último argumento devuelto es un error
.
func DoAndReturnSomething() (string, error) {
if os.Getenv("ERROR") == "1" {
return "", errors.New("The method failed")
}
// The method succeeded.
return "Success!", nil
}
Utiliza múltiples asignaciones de variables para verificar si el método falló.
result, err := DoAndReturnSomething()
if err != nil {
panic(err)
}
// This is executed only if the method didn't return an error
fmt.Println(result)
Si no está interesado en el error, simplemente puede ignorarlo asignándolo a _
.
result, _ := DoAndReturnSomething()
fmt.Println(result)
Por supuesto, ignorar un error puede tener serias implicaciones. Por lo tanto, esto generalmente no se recomienda.
Si tiene varias llamadas a métodos, y uno o más métodos en la cadena pueden devolver un error, debe propagar el error al primer nivel que puede manejarlo.
func Foo() error {
return errors.New("I failed!")
}
func Bar() (string, error) {
err := Foo()
if err != nil {
return "", err
}
return "I succeeded", nil
}
func Baz() (string, string, error) {
res, err := Bar()
if err != nil {
return "", "", err
}
return "Foo", "Bar", nil
}
Recuperándose del pánico
Un error común es declarar un sector y comenzar a solicitar índices sin inicializarlo, lo que lleva a un pánico de "índice fuera de rango". El siguiente código explica cómo recuperarse del pánico sin salir del programa, que es el comportamiento normal de un pánico. En la mayoría de las situaciones, devolver un error de esta manera en lugar de salir del programa en una situación de pánico solo es útil para fines de desarrollo o prueba.
type Foo struct {
Is []int
}
func main() {
fp := &Foo{}
if err := fp.Panic(); err != nil {
fmt.Printf("Error: %v", err)
}
fmt.Println("ok")
}
func (fp *Foo) Panic() (err error) {
defer PanicRecovery(&err)
fp.Is[0] = 5
return nil
}
func PanicRecovery(err *error) {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
//fmt.Println("Panicing")
//panic(r)
*err = r.(error)
} else {
*err = r.(error)
}
}
}
El uso de una función separada (en lugar del cierre) permite la reutilización de la misma función en otras funciones propensas al pánico.