英文:
Returning File Pointer in Golang
问题
我仍然在努力理解 Golang 的基础知识。
考虑以下示例代码:
func OpenOutputFile(name string) (fp *os.File) {
fp, err := os.Create(name)
if err != nil {
panic(err)
}
defer func() {
if err := fp.Close(); err != nil {
panic(err)
}
}()
return fp
}
我会假设调用:
fp := OpenOutputFile("output.txt")
会使 fp
成为一个文件指针 (*os.File
),这样我就可以在另一个函数中调用类似这样的语句:
io.WriteString(fp, "Hello World")
但是,在调用这个方法时,会生成错误:
0 write output.txt: bad file descriptor
所以看起来返回的指针无效。我该如何返回一个正确形式的指针以便在 io.WriteString
中使用?
感谢您的帮助!
需要注意的是:当文件指针的创建和写入文件指针存在于同一个方法中时,一切都按预期执行。将逻辑拆分为一个函数会导致它无法按预期工作。
英文:
I'm still struggling with the basics of Golang.
Consider the following sample code:
func OpenOutputFile(name string) (fp *os.File) {
fp, err := os.Create(name)
if err != nil {
panic(err)
}
defer func() {
if err := fp.Close(); err != nil {
panic(err)
}
}()
return fp
}
I would assume that calling:
fp := OpenOutputFile("output.txt")
would now make fp
a file pointer (*os.File
), so that I could call a statement like:
io.WriteString(fp, "Hello World")
In another function. But when calling this method, the error is generated:
0 write output.txt: bad file descriptor
So it appears that the pointer returned is not valid. How can I return a properly formed pointer to use with io.WriteString
?
I appreciate the help!
Of note: Everything executes as intended when the creation of the file pointer and the writing to the file pointer exists in the same method. Breaking the logic into a function causes it to not behave as intended.
答案1
得分: 7
《Go编程语言规范》
延迟语句
"defer"语句会调用一个函数,该函数的执行被延迟到包围它的函数返回的时刻,无论是因为包围的函数执行了一个返回语句,到达了函数体的末尾,还是因为相应的goroutine正在恐慌。
每次执行"defer"语句时,函数值和调用的参数会像平常一样被求值并重新保存,但实际的函数不会被调用。相反,延迟函数会在包围的函数返回之前立即被调用,按照它们被延迟的相反顺序。如果延迟函数的值求值为nil,在调用函数时会引发恐慌,而不是在执行"defer"语句时。
例如,如果延迟函数是一个函数字面量,并且包围的函数具有在字面量内部可见的命名结果参数,延迟函数可以在返回之前访问和修改结果参数。如果延迟函数有任何返回值,在函数完成时它们会被丢弃。
func OpenOutputFile(name string) (fp *os.File) {
fp, err := os.Create(name)
if err != nil {
panic(err)
}
defer func() {
if err := fp.Close(); err != nil {
panic(err)
}
}()
return fp
}
你打开文件
fp, err := os.Create(name)
你关闭文件
err := fp.Close()
在Close
之后,fp
不再指向有效的文件描述符。
英文:
> The Go Programming Language Specification
>
> Defer statements
>
> A "defer" statement invokes a function whose execution is deferred to
> the moment the surrounding function returns, either because the
> surrounding function executed a return statement, reached the end of
> its function body, or because the corresponding goroutine is
> panicking.
>
> Each time a "defer" statement executes, the function value and
> parameters to the call are evaluated as usual and saved anew but the
> actual function is not invoked. Instead, deferred functions are
> invoked immediately before the surrounding function returns, in the
> reverse order they were deferred. If a deferred function value
> evaluates to nil, execution panics when the function is invoked, not
> when the "defer" statement is executed.
>
> For instance, if the deferred function is a function literal and the
> surrounding function has named result parameters that are in scope
> within the literal, the deferred function may access and modify the
> result parameters before they are returned. If the deferred function
> has any return values, they are discarded when the function completes.
func OpenOutputFile(name string) (fp *os.File) {
fp, err := os.Create(name)
if err != nil {
panic(err)
}
defer func() {
if err := fp.Close(); err != nil {
panic(err)
}
}()
return fp
}
You open the file
fp, err := os.Create(name)
You close the file
err := fp.Close()
After the Close
, fp
no longer points to a valid file descriptor.
答案2
得分: 0
将close函数返回并延迟在更高的作用域中对我起了作用。不过我不确定这是否是Go语言中的一个好实践。它依赖于在原始函数之外关闭/延迟执行。
func OpenFileFromArgs() (*os.File, func()) {
if len(os.Args) < 2 {
panic("未提供输入文件")
}
inputFilePath := os.Args[1]
stat, err := os.Stat(inputFilePath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
panic(fmt.Sprintf("文件 %s 不存在", inputFilePath))
} else {
panic(fmt.Sprintf("错误:%v", err))
}
}
if stat.IsDir() {
panic("提供的路径是一个目录")
}
inputFile, err := os.Open(inputFilePath)
closeFn := func() {
err := inputFile.Close()
if err != nil {
panic("无法关闭输入文件")
}
}
return inputFile, closeFn
}
然后在更高的作用域中:
inputFile, closeFn := library.OpenFileFromArgs()
defer closeFn()
通过逐步调试确认 - 在程序结束时,延迟的关闭函数被正确调用,文件描述符被关闭。
英文:
Returning the close function and deferring it in higher scope worked for me. I don't know if this is a good practice in Go though. It relies on closing/deferring outside of original function:
func OpenFileFromArgs() (*os.File, func()) {
if len(os.Args) < 2 {
panic("input file not provided")
}
inputFilePath := os.Args[1]
stat, err := os.Stat(inputFilePath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
panic(fmt.Sprintf("file %s doens't exist", inputFilePath))
} else {
panic(fmt.Sprintf("error: %v", err))
}
}
if stat.IsDir() {
panic("provided path is a directory")
}
inputFile, err := os.Open(inputFilePath)
closeFn := func() {
err := inputFile.Close()
if err != nil {
panic("failed to close input file")
}
}
return inputFile, closeFn
and then in the higher scope:
inputFile, closeFn := library.OpenFileFromArgs()
defer closeFn()
Confirmed using step by step debugger - at program end, the deferred close function is correctly called and file descriptor closed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论