jueves, 28 de noviembre de 2019

Defer, Panic, y Recover en Go


La visión de Go es particular, la verdad es que no se si es buena o mala pero claramente es particular. Para Go un error es algo que puede pasar y es parte del resultado. Para manejar esta visión tiene 3 palabras reservadas : Defer, Panic, y Recover.

Defer nos permiten pensar en cerrar cada archivo (por ejemplo) justo después de abrirlo, garantizando que, independientemente del número de declaraciones return en la función, los archivos se cerrarán. Es como el final de java, veamos un ejemplo :

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()

    return io.Copy(dst, src)
}

El comportamiento de las declaraciones diferidas es sencillo y predecible. Hay tres reglas simples:

1. Los argumentos de una función diferida se evalúan cuando se evalúa la declaración diferida.

En este ejemplo, la expresión "i" se evalúa cuando se llama a Println. La llamada diferida imprimirá "0" después de que regrese la función.

func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

2. Las llamadas a funciones diferidas se ejecutan en el orden último en entrar, primero en salir después de que vuelve la función circundante.

Esta función imprime "3210":

func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

3.Las funciones diferidas pueden leer y asignar a los valores de retorno nombrados de la función de retorno.

En este ejemplo, una función diferida incrementa el valor de retorno i después de que regresa la función circundante. Por lo tanto, esta función devuelve 2:

func c() (i int) {
    defer func() { i++ }()
    return 1
}

Esto es conveniente para modificar el valor de retorno de error de una función; Veremos un ejemplo de esto en breve.

Panic es una función incorporada que detiene el flujo de control ordinario y comienza a entrar en pánico. Cuando la función F llama Panic, la ejecución de F se detiene, las funciones diferidas en F se ejecutan normalmente y luego F vuelve a su llamador. Para la persona que llama, F entonces se comporta como una llamada al pánico. El proceso continúa en la pila hasta que todas las funciones en la rutina actual han regresado, momento en el cual el programa falla. El pánico puede iniciarse invocando el pánico directamente. También pueden ser causados ​​por errores de tiempo de ejecución, como accesos de matriz fuera de los límites.

Recover es una función incorporada que recupera el control de una gorutina en pánico. La recuperación solo es útil dentro de las funciones diferidas. Durante la ejecución normal, una llamada a recuperar devolverá nulo y no tendrá otro efecto. Si la rutina actual está en pánico, una llamada a Recover capturará el valor dado al pánico y reanudará la ejecución normal.

Veamos un ejemplo todo mezclado:

package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}

La función g toma el int i, y entra en pánico si i es mayor que 3, o se llama a sí mismo con el argumento i + 1. La función f difiere una función que llama a recovery e imprime el valor recuperado (si no es nulo).

El programa generará:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

Y eso es todo amigos!

No hay comentarios.:

Publicar un comentario