Translate

lunes, 23 de noviembre de 2020

Manejo de errores en Go vs Rust Parte 2


Las segundas partes a veces son buenas como terminator 2. Por lo tanto seguimos con el manejo de errores de Go y Rust. 

En Go, una declaración defer empuja una llamada de función a una lista. La lista de llamadas guardadas se ejecuta después de que regrese la función circundante. Defer se usa comúnmente para simplificar funciones que realizan varias acciones de limpieza.

Por ejemplo, veamos una función que abre dos archivos y copia el contenido de un archivo al otro:

func CopyFile(dstName, srcName string) (written int64, err error) {

    src, err := os.Open(srcName)

    if err != nil {

        return

    }

    dst, err := os.Create(dstName)

    if err != nil {

        return

    }

    written, err = io.Copy(dst, src)

    dst.Close()

    src.Close()

    return

}

Esto funciona, pero hay un error. Si la llamada a os.Create falla, la función regresará sin cerrar el archivo fuente. Esto puede remediarse fácilmente llamando a src.Close antes de la segunda declaración de retorno, pero si la función fuera más compleja, el problema podría no ser detectado y resuelto tan fácilmente. Al introducir declaraciones diferidas, podemos asegurarnos de que los archivos siempre estén cerrados:

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)

}

Las declaraciones diferidas nos permiten pensar en cerrar cada archivo justo después de abrirlo, garantizando que, independientemente del número de declaraciones devueltas en la función, los archivos se cerrarán.

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

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

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

func a() {

    i := 0

    defer fmt.Println(i)

    i++

    return

}

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

Esta función imprime "3210":

func b() {

    for i := 0; i < 4; i++ {

        defer fmt.Print(i)

    }

}

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. A mi me suena como que defer es el finaly de una expesión try-catch de Java o C++. 

Pero Rust no tiene tampoco la estructura try-catch de Java o C++ entonces ¿como podríamos simular el finally? o cerrar un archivo luego de utilidad. En realidad Rust carece de estas estructuras porque no son necesarias. Por ejemplo podemos hacer algo así : 

fn main() {

    let do_steps = || -> Result<(), MyError> {

        do_step_1()?;

        do_step_2()?;

        do_step_3()?;

        Ok(())

    };


    if let Err(_err) = do_steps() {

        println!("Failed to perform necessary steps");

    }

     runFinallyMethod(); 

}

Y listo! Bueno, vamos a seguir en proximos posts...