I find the provision of named returned variables in Go to be a useful feature, because it can avoid the separate declaration of a variable or variables. However, in some instances I want to return a different variable to the variable declared in the function as the return-variable. That appears to work ok, however I do find it a little strange to declare a return variable and then return something else.
While writing a test program to help learn Go (not the one below), I found it a little annoying specifying the return variable in the return statement of functions returning multiple variables. Particularly so, because the variables had been named in the function declaration. I now find while posting this, that it appears that where there are named return variables, they don't need to be used in the return statement, just "return" will suffice and will implicitly use the named variables. That I find this a great feature.
So, although I have possibly partly answered my own question, could someone advise if my usage below is acceptable? I'm sure this is documented, but I haven't come across it, and it doesn't appear to be in the reference-book that I purchased which I think overlooks this feature.
Basically, the rule appears to be (as far as I can determine), that where named return variables are used, that the function statement declares the variables, and also the function can optionally implicitly uses them as the return values, however this can be overridden by using explicit return values.
Example Program :
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'"
}
}
Your usage is absolutely fine and you'll find plenty of similar examples in the Go source code.
I'll attempt to explain how the return statement actually works in Go to give a deeper appreciation of why. It is useful to have a think about how Go implements parameter passing and return from functions. Once you understand that you'll understand why named return variables are so natural.
All arguments to functions and all return values from functions are passed on the stack in Go. This varies from C which usually passes some parameters in registers. When a function is called in Go the caller makes space on the stack for both the arguments and the return values then calls the function.
Specifically, when this function is called, which has 3 input parameters a, b, c and two return values
func f(a int, b int, c int) (int, int)
The stack will look like this (low memory address at the top)
* a
* b
* c
* space for return parameter 1
* space for return parameter 2
Now it is obvious that naming your return parameter just names those locations on the stack.
func f(a int, b int, c int) (x int, y int)
* a
* b
* c
* x
* y
It should now also be obvious what an empty return
statement does - it just returns to the caller with whatever the values of x and y are.
Now for some disassembly! Compiling this with 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
}
Gives
--- 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 ,
Both functions assemble to pretty much the same code. You can see quite clearly the declarations of a,b,c,x,y
on the stack in g
, though in f
, the return values are anonymous anon3
and anon4
.
Note: CL 20024 (March 2016, for Go 1.7) clarifies the usage of named return values and illustrates within the code base of go itself when its usage is appropriate:
all
: remove public named return values when uselessNamed returned values should only be used on public funcs and methods when it contributes to the documentation.
Named return values should not be used if they're only saving the programmer a few lines of code inside the body of the function, especially if that means there's stutter in the documentation or it was only there so the programmer could use a naked return statement. (Naked returns should not be used except in very small functions)
This change is a manual audit & cleanup of public func signatures.
Signatures were not changed if:
- the func was private (wouldn't be in public godoc)
- the documentation referenced it
For instance, archive/zip/reader.go#Open() used
func (f *File) Open() (rc io.ReadCloser, err error) {
It now uses:
func (f *File) Open() (io.ReadCloser, error) {
Its named return values didn't add anything to its documentation, which was:
// Open returns a `ReadCloser` that provides access to the File's contents.
// Multiple files may be read concurrently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With