DevilKing's blog

冷灯看剑,剑上几分功名?炉香无需计苍生,纵一穿烟逝,万丈云埋,孤阳还照古陵

0%

Golang Return

Golang Internals Part 2: Nice benefits of named return values

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type objectInfo struct {
arg1 int64
arg2 uint64
arg3 string
arg4 []int
}
func NoNamedReturnParams(i int) (objectInfo) {

if i == 1 {
// Do one thing
return objectInfo{}
}

if i == 2 {
// Do another thing
return objectInfo{}
}

if i == 3 {
// Do one more thing still
return objectInfo{}
}

// Normal return
return objectInfo{}
}

actual code that the golang compiler generates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
"".NoNamedReturnParams t=1 size=243 args=0x40 locals=0x0
0x0000 TEXT "".NoNamedReturnParams(SB), $0-64
0x0000 MOVQ $0, "".~r1+16(FP)
0x0009 LEAQ "".~r1+24(FP), DI
0x000e XORPS X0, X0
0x0011 ADDQ $-16, DI
0x0015 DUFFZERO $288
0x0028 MOVQ "".i+8(FP), AX
0x002d CMPQ AX, $1
0x0031 JEQ $0, 199
0x0037 CMPQ AX, $2
0x003b JEQ $0, 155
0x003d CMPQ AX, $3
0x0041 JNE 111
0x0043 MOVQ "".statictmp_2(SB), AX
0x004a MOVQ AX, "".~r1+16(FP)
0x004f LEAQ "".~r1+24(FP), DI
0x0054 LEAQ "".statictmp_2+8(SB), SI
0x005b DUFFCOPY $854
0x006e RET
0x006f MOVQ "".statictmp_3(SB), AX
0x0076 MOVQ AX, "".~r1+16(FP)
0x007b LEAQ "".~r1+24(FP), DI
0x0080 LEAQ "".statictmp_3+8(SB), SI
0x0087 DUFFCOPY $854
0x009a RET
0x009b MOVQ "".statictmp_1(SB), AX
0x00a2 MOVQ AX, "".~r1+16(FP)
0x00a7 LEAQ "".~r1+24(FP), DI
0x00ac LEAQ "".statictmp_1+8(SB), SI
0x00b3 DUFFCOPY $854
0x00c6 RET
0x00c7 MOVQ "".statictmp_0(SB), AX
0x00ce MOVQ AX, "".~r1+16(FP)
0x00d3 LEAQ "".~r1+24(FP), DI
0x00d8 LEAQ "".statictmp_0+8(SB), SI
0x00df DUFFCOPY $854
0x00f2 RET

All fine and dandy, but if that looks a bit repetitive to you, you are quite right. Essentially for each of the return statements the object to be returned is more or less allocated/initialized (or more precisely copied via the DUFFCOPYmacro).

naked return feature of golang

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func NamedReturnParams(i int) (oi objectInfo) {

if i == 1 {
// Do one thing
return
}

if i == 2 {
// Do another thing
return
}

if i == 3 {
// Do one more thing still
return
}

// Normal return
return
}

It reduces the size of the function down from 243 to 67 bytes. Also as an additional benefit you will save some CPU cycles upon exiting out because there is no need anymore to do anything in order to setup the return value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// parse credentialHeader string into its structured form.
func parseCredentialHeader(credElement string) (ch credentialHeader) {
creds := strings.Split(strings.TrimSpace(credElement), "=")
if len(creds) != 2 {
return
}
if creds[0] != "Credential" {
return
}
credElements := strings.Split(strings.TrimSpace(creds[1]), "/")
if len(credElements) != 5 {
return
}
if false /*!isAccessKeyValid(credElements[0])*/ {
return
}
// Save access key id.
cred := credentialHeader{
accessKey: credElements[0],
}
var e error
cred.scope.date, e = time.Parse(yyyymmdd, credElements[1])
if e != nil {
return
}
cred.scope.region = credElements[2]
if credElements[3] != "s3" {
return
}
cred.scope.service = credElements[3]
if credElements[4] != "aws4_request" {
return
}
cred.scope.request = credElements[4]
return cred
}

隐含的return value的方式

Note that actually the ch variable is a normal local variable just like any other local variable that is defined within the function. And as such you can change its value from the default ‘zero’ state (but of course then the modified version will be returned upon exiting out).

others uses of named return values

As pointed out by several persons, another benefit of named return values is the use in closures (i.e. defer statements). Thus one may access the named return value in a function that is called as the result of a defer statement and act accordingly.

conclusion

So we will be gradually adopting named return values more and more, both for new code as well as for existing code.

future to integration with gofmt to modify the source automatically