godefer-keyword

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


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?


Solution

  • 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")
    }