英文:
How to defer resource cleanup when that resource outlives the scope of the surrounding function?
问题
让我们以这段代码为例,该代码使记录器将日志写入本地文件而不是标准输出:
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)
作者将这段代码直接放在main()
函数中,这样它就能按预期工作。但是,如果我想将这段代码放入一个专用函数中,然后main()
函数调用该函数,那么它将不再起作用,因为在记录器被使用之前,f.Close()
将被调用。
例如(如果上面的代码现在在一个名为logToFile()
的函数中):
main() {
logToFile()
log.Print("I'm going to end up in stdout\n")
}
这段代码能够移动到自己的函数中,并且仍然按预期工作吗?
我在打开/关闭数据库连接方面也遇到了同样的情况。似乎唯一的方法是在main()
函数内部执行这两个操作,但我认为如果我们能将代码分成函数,代码看起来会更清晰、更符合单一职责原则。在Go语言中,这样做是否可行?
英文:
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?
答案1
得分: 6
你是不是在寻找这样的代码?
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()
}
}
使用方法:
func main() {
closerFn := logToFile("filename")
defer closerFn()
log.Print("logs to file\n")
}
这段代码定义了一个closerFunc
类型和一个logToFile
函数。logToFile
函数接受一个文件路径作为参数,打开该文件并将日志输出到文件中。它返回一个闭包函数,用于关闭文件。在main
函数中,我们可以使用logToFile
函数来初始化一个closerFn
闭包函数,并使用defer
关键字在函数结束时关闭文件。然后,我们可以使用log.Print
函数将日志输出到文件中。
英文:
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")
}
答案2
得分: 0
一种选择是使用延续传递风格,将要在defer
块中执行的代码作为显式参数传递:
func withLogToFile(filename string, body func()) {
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Fatal(err)
}
prevOut := log.Writer()
log.SetOutput(f)
defer func() {
log.SetOutput(prevOut)
if err := f.Close(); err != nil {
log.Fatal(err)
}
}()
body()
}
然后调用的地方变成:
func main() {
withLogToFile(filename, func() {
log.Print("我将会出现在", filename)
})
}
(https://play.golang.org/p/ebCvtzufU5U)
英文:
One option is to use continuation-passing style, passing the code to be executed within the defer
block as an explicit argument:
func withLogToFile(filename string, body func()) {
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Fatal(err)
}
prevOut := log.Writer()
log.SetOutput(f)
defer func() {
log.SetOutput(prevOut)
if err := f.Close(); err != nil {
log.Fatal(err)
}
}()
body()
}
Then the call site becomes:
func main() {
withLogToFile(filename, func() {
log.Print("I'm going to end up in ", filename)
})
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论