写法

如果 func 定义,需要指定参数名,应该将返回参数用 () 包起来

1
2
3
4
5
// 这样写法更简洁,不用在方法体内在定义多余的变量
func All() (users []User) {
database.DB.Find(&users)
return users
}

好处

  • 命名返回值在函数开始时会被自动初始化
  • 命名返回值可以使函数更加清晰和自说明
  • 命名返回值的作用域仅限于函数体内部

注意事项

  • 允许在函数内部修改返回值
  • 函数有命名返回值,并且在函数体内部存在与命名返回值同名的局部变量,那么局部变量将覆盖命名返回值
  • 命名返回值可以在函数体内部进行修改,但如果在函数体内部使用 return 语句显式指定返回值,那么它将覆盖命名返回值的值

示例

  • 在函数内部修改返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func test() (i int) { // 这里在 `test` 函数开始时,i 就会被初始化
// i := 1 如果给 `i` 重新初始化,编译不通过,所以也就证明了 `命名式返回值会在函数开始时,初始化返回值为类型的零值`
i += 1 // 这里的局部变量与命名返回值的变量相同,所以这里的局部变量 `i` 会覆盖返回值的的 `i` 变量,这也是导致返回结果是1的原因
return
}

func main() {
fmt.Println("return", test()) // return 1
}
  • 在函数体内部使用 return 语句显式指定返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func test() (i int) { // 这里在 `test` 函数开始时,i 就会被初始化
x, i := 0, 1
return x // 这里在函数提内部修改的命名返回值变量 `i`,并且显式的返回变量 `x` ,命名返回值的 `i`被覆盖了,代码看起来没啥用🐶
// 但是实际就是这样,这个例子可能不清晰
}

func main() {
fmt.Println("return", test()) // return 0
}
  • 对比例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 命名返回值
package main

import "fmt"

func test() (i int) {
i = 0
defer func() {
i += 1
fmt.Println("defer")
}()
return // 或者 return i ,结果是一样的
}

func main() {
fmt.Println("return", test()) // defer,return 1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 命名返回值
package main

import "fmt"

func test() int {
i := 0
defer func() {
i += 1
fmt.Println("defer")
}()
return i
}

func main() {
fmt.Println("return", test()) // defer,return 0
}

通过上面两个例子对比,我们可以看出 i 返回值是不同的,首先我们要知道 defer 的执行时间是在函数返回之前,其次就是命名返回值是可以在函数能部修改的,可以简单理解为 如果函数提内部出现的局部变量和命名返回值的变量名相同,那我们可以认为这个局部变量就是函数返回值的一部分,也就是命名变量 i,也就是说局部变量 i是命名返回值变量 i,所以返回值不一样