Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to defer resource cleanup when that resource outlives the scope of the surrounding function?

Tags:

go

deferred

Let's take the example of this piece of code that makes the logger write to a local file instead of the standard output:

f, err := os.OpenFile("filename", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
        log.Fatal(err)
}   

defer f.Close()

log.SetOutput(f)

The author is putting this code straight into the main() function, which makes it work as intended. But if I wanted to put this code into a a dedicated function which main() may then call, then it would no longer work, because the f.Close() would be called before the logger ever gets used.

E.g. (if the code above is now in a function called logToFile()):

main() {
   logToFile()
   log.Print("I'm going to end up in stdout\n")
}

Can this be moved into its own function, and still have it work as intended?

I've had the same situation with opening/closing of a database connection. And it seems like the only way is to do both of these things inside the main(), but I think the code would look cleaner and more SoC if we could divide it into functions. Is this a no-no in Go?

like image 532
Nunoki Avatar asked Jan 25 '23 07:01

Nunoki


1 Answers

You looking for something like this?

type closerFunc func() error

func logToFile(path string) closerFunc {
    f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        log.Fatal(err)
    }   

    log.SetOutput(f)

    return func() error {
        return f.Close()
    }
}

To use:

func main() {
    closerFn := logToFile("filename")
    defer closerFn()

    log.Print("logs to file\n")
}
like image 154
colm.anseo Avatar answered May 16 '23 05:05

colm.anseo