Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to gently defer execution of a function that might return an error?

Tags:

go

Most cleanup functions, especially those related to the IO operations, return an error, and normally we'd prefer to defer their execution in case if we'd not forget to call them when we're done with acquired resources. For example, at some point in the code we might write something like this:

var r *SomeResource
var err error
if r, err = Open(/* parameters */); err != nil {
    return nil, err
}
defer r.Close() // This might return an error

It seems that if Close function returns an error, it'll be ignored. How can we gently process the returned error from such a function?

like image 407
Alirus Avatar asked Dec 11 '22 04:12

Alirus


2 Answers

Using defer with a func() {}() like so.

var r *SomeResource
var err error
if r, err = Open(/* parameters */); err != nil {
    return nil, err
}
defer func() {
    if err = r.Close(); err != nil {
        fmt.Printf("ERROR: %v", err)
    }
}()
like image 199
Seaskyways Avatar answered Jan 21 '23 12:01

Seaskyways


Fail gracefully with an error. Report the first error. Don't overwrite earlier errors. For example,

package main

import (
    "fmt"
    "os"
)

func demo() (name string, err error) {
    filename := `test.file`
    f, err := os.Open(filename)
    if err != nil {
        return "", err
    }
    defer func() {
        e := f.Close()
        if e != nil {
            if err == nil {
                err = e
            }
        }
    }()

    // do someting with the file
    name = f.Name()
    fi, err := f.Stat()
    if err != nil {
        return name, err
    }
    if fi.Size() == 0 {
        err = fmt.Errorf("%s: empty file", filename)
        return name, err
    }

    return name, err
}

func main() {
    name, err := demo()
    fmt.Println(name, err)
}
like image 28
peterSO Avatar answered Jan 21 '23 11:01

peterSO