Go中命名返回变量的预期用途是什么?

Go中命名返回变量的预期用途是什么?,go,Go,我发现在Go中提供命名返回变量是一个有用的特性,因为它可以避免单独声明一个或多个变量。但是,在某些情况下,我希望将不同的变量返回到函数中声明为返回变量的变量。这看起来不错,但是我发现声明一个返回变量然后返回其他变量有点奇怪 在编写一个帮助学习Go的测试程序(不是下面的程序)时,我发现在返回多个变量的函数的return语句中指定返回变量有点烦人。尤其如此,因为变量已在函数声明中命名。我现在发现,在发布这篇文章时,似乎在有命名返回变量的地方,它们不需要在return语句中使用,只需“return”就

我发现在Go中提供命名返回变量是一个有用的特性,因为它可以避免单独声明一个或多个变量。但是,在某些情况下,我希望将不同的变量返回到函数中声明为返回变量的变量。这看起来不错,但是我发现声明一个返回变量然后返回其他变量有点奇怪

在编写一个帮助学习Go的测试程序(不是下面的程序)时,我发现在返回多个变量的函数的return语句中指定返回变量有点烦人。尤其如此,因为变量已在函数声明中命名。我现在发现,在发布这篇文章时,似乎在有命名返回变量的地方,它们不需要在return语句中使用,只需“return”就足够了,并将隐式使用命名变量。我觉得这是一个很好的特点

因此,尽管我可能已经部分回答了我自己的问题,有人能告诉我我下面的用法是否可以接受吗?我确信这是有记录的,但我没有发现,而且我购买的参考书中似乎没有这一点,我认为它忽略了这一功能

基本上,规则似乎是(据我所知),在使用命名返回变量的地方,函数语句声明变量,函数也可以选择隐式使用它们作为返回值,但是这可以通过使用显式返回值来覆盖

示例程序:

package main

func main() {
    var sVar1, sVar2 string
    println("Test Function return-values")
    sVar1, sVar2 = fGetVal(1)
    println("This was returned for '1' : " + sVar1 + ", " + sVar2)
    sVar1, sVar2 = fGetVal(2)
    println("This was returned for '2' : " + sVar1 + ", " + sVar2)
}

func fGetVal(iSeln int) (sReturn1 string, sReturn2 string) {
    sReturn1 = "This is 'sReturn1'"
    sReturn2 = "This is 'sReturn2'"

    switch iSeln {
        case 1  :  return
        default : return "This is not 'sReturn1'", "This is not 'sReturn2'"
    }
}

您的使用非常好,在Go源代码中可以找到大量类似的示例

我将试图解释return语句在Go中是如何工作的,以便更深入地理解其中的原因。考虑一下Go如何实现参数传递和函数返回是很有用的。一旦您了解了这一点,您就会明白为什么命名返回变量如此自然

函数的所有参数和函数的所有返回值都在Go中的堆栈上传递。这与C不同,C通常在寄存器中传递一些参数。在Go中调用函数时,调用方在堆栈上为参数和返回值留出空间,然后调用该函数

具体来说,当调用此函数时,它有3个输入参数a、b、c和两个返回值

func f(a int, b int, c int) (int, int)
堆栈将如下所示(顶部的低内存地址)

现在很明显,命名返回参数只是命名堆栈上的那些位置

func f(a int, b int, c int) (x int, y int)

* a
* b
* c
* x
* y
现在,空的
return
语句的作用也应该很明显了——它只返回给调用方x和y的值

现在进行一些拆卸!使用
go build-gcflags-S test.go编译此文件

package a

func f(a int, b int, c int) (int, int) {
    return a, 0
}

func g(a int, b int, c int) (x int, y int) {
    x = a
    return
}
给予


这两个函数组合成几乎相同的代码。在
g
中,您可以清楚地看到堆栈上
a、b、c、x、y的声明,尽管在
f
中,返回值是匿名的
anon3
anon4
是的,这是完全可以接受的。我通常使用命名返回变量来确保延迟错误处理中的默认返回,以确保最小可行返回,如下面的示例:

//execute an one to one reflection + cache operation
func (cacheSpot CacheSpot) callOneToOne(originalIns []reflect.Value) (returnValue []reflect.Value) {

    defer func() { //assure for not panicking
        if r := recover(); r != nil {
            log.Error("Recovering! Error trying recover cached values!! y %v", r)

            //calling a original function
            returnValue = reflect.ValueOf(cacheSpot.OriginalFunc).Call(originalIns)
        }
    }()

    //... doing a really nasty reflection operation, trying to cache results. Very error prone. Maybe panic

    return arrValues //.. it's ok, arrValues achieved
}
注意:澄清命名返回值的用法,并在go自身的代码库中说明其用法是否合适:

all
:删除无用的公共命名返回值 命名返回值只能用于公共函数和方法 当它提供给文档时

如果命名返回值仅用于保存 在函数体中编写几行代码, 特别是如果这意味着文档中有口吃或 只有这样程序员才能使用裸体返回 陈述(除非是在非常小的范围内,否则不应使用裸申报表。) 功能)

此更改是对公共func签名的手动审核和清理

如果出现以下情况,则不会更改签名:

  • func是私有的(不会公开在godoc中)
  • 文件引用了它

例如,使用

它现在使用:

func (f *File) Open() (io.ReadCloser, error) {
其命名的返回值没有向其文档中添加任何内容,即:

// Open returns a `ReadCloser` that provides access to the File's contents.
// Multiple files may be read concurrently.

感谢您的详细解释。有趣的是,了解命名返回将出现在堆栈上。
func (f *File) Open() (rc io.ReadCloser, err error) {
func (f *File) Open() (io.ReadCloser, error) {
// Open returns a `ReadCloser` that provides access to the File's contents.
// Multiple files may be read concurrently.