我发现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'"
}
}
答案 0 :(得分:17)
你的用法非常好,你会在Go源代码中找到很多类似的例子。
我将尝试解释返回语句在Go中如何实际起作用以更深入地了解原因。考虑Go如何实现参数传递和从函数返回是很有用的。一旦你明白你就会明白为什么命名的返回变量是如此自然。
函数的所有参数和函数的所有返回值都在Go中的堆栈上传递。这与通常在寄存器中传递一些参数的C不同。当在Go中调用函数时,调用者在堆栈上为参数和返回值创建空间,然后调用该函数。
具体来说,当调用此函数时,它有3个输入参数a,b,c和两个返回值
func f(a int, b int, c int) (int, int)
堆栈看起来像这样(顶部的低内存地址)
* a
* b
* c
* space for return parameter 1
* space for return parameter 2
现在很明显,命名返回参数只是在堆栈上命名那些位置。
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
}
给出
--- prog list "f" ---
0000 (test.go:3) TEXT f+0(SB),$0-40
0001 (test.go:3) LOCALS ,$0
0002 (test.go:3) TYPE a+0(FP){int},$8
0003 (test.go:3) TYPE b+8(FP){int},$8
0004 (test.go:3) TYPE c+16(FP){int},$8
0005 (test.go:3) TYPE ~anon3+24(FP){int},$8
0006 (test.go:3) TYPE ~anon4+32(FP){int},$8
0007 (test.go:4) MOVQ a+0(FP),BX
0008 (test.go:4) MOVQ BX,~anon3+24(FP)
0009 (test.go:4) MOVQ $0,~anon4+32(FP)
0010 (test.go:4) RET ,
--- prog list "g" ---
0011 (test.go:7) TEXT g+0(SB),$0-40
0012 (test.go:7) LOCALS ,$0
0013 (test.go:7) TYPE a+0(FP){int},$8
0014 (test.go:7) TYPE b+8(FP){int},$8
0015 (test.go:7) TYPE c+16(FP){int},$8
0016 (test.go:7) TYPE x+24(FP){int},$8
0017 (test.go:7) TYPE y+32(FP){int},$8
0018 (test.go:7) MOVQ $0,y+32(FP)
0019 (test.go:8) MOVQ a+0(FP),BX
0020 (test.go:8) MOVQ BX,x+24(FP)
0021 (test.go:9) RET ,
两个函数汇编到几乎相同的代码。您可以在a,b,c,x,y
中清楚地看到g
在堆栈上的声明,但在f
中,返回值是匿名anon3
和anon4
。
答案 1 :(得分:2)
注意:CL 20024 (March 2016, for Go 1.7)阐明了命名返回值的用法,并在其使用适当时在go本身的代码库中说明:
$(document).on("scroll", function() { if ($(document).scrollTop() > 100) { $(".profiledetails").addClass("fixpos"); } else { $(".profiledetails").removeClass("fixpos") } });
:无用时删除公共命名的返回值命名的返回值应仅用于公共函数和方法 当它有助于文档。
如果仅保存命名的返回值,则不应使用它们 程序员在函数体内有几行代码, 特别是如果这意味着文档中的口吃或它 只有在那里程序员可以使用裸回报 声明。 (除非非常小,否则不应使用裸体返回 功能)
此更改是手动审核&公共功能签名的清理。
如果出现以下情况,则不会更改签名:
- func是私有的(不会在公共godoc中)
- 文档引用它
例如,archive/zip/reader.go#Open()使用了
.profiledetails
现在使用:
all
它的命名返回值没有在其文档中添加任何内容,即:
func (f *File) Open() (rc io.ReadCloser, err error) {
答案 2 :(得分:0)
是的,它完全可以接受。我通常使用命名返回变量来确保延迟错误处理中的默认返回,以确保最小可行返回,如下例所示:
//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
}