结构体是什么
在 Go 语言中有个基本类型,开发者们称之为结构体(struct)。是 Go 语言中非常常用的,基本定义:
type struct_variable_type struct {
member definition
member definition
...
member definition
}
简单示例:
package main
import "fmt"
type Vertex struct {
Name1 string
Name2 string
}
func main() {
v := Vertex{"脑子进了", "煎鱼"}
v.Name2 = "蒸鱼"
fmt.Println(v.Name2)
}
输出结果:
蒸鱼
这部分属于基础知识,因此不再过多解释。如果看不懂,建议重学 Go 语言语法基础。
结构体和指针调用
讲解前置概要后,直接进入本文主题。如下例子:
type MyStruct struct {
Name string
}
func (s MyStruct) SetName1(name string) {
s.Name = name
}
func (s *MyStruct) SetName2(name string) {
s.Name = name
}
UserSetName1SetName2
进一步延伸,这两者有什么区别,什么情况下用哪种,有没有什么注意事项?
注:很巧,我有一个朋友,当年刚上手 Go 语言时,就纠结过这个问题。
两者区别
从许多小伙伴的反馈来看,这两个例子之间的区别可能会让人感到困惑,经常会有人纠结要不要使用 “指针”,又担心 GC 什么的。
实际上情况没那么复杂,看看下面的例子:
func (s MyStruct) SetName1(name string)
func (s *MyStruct) SetName2(name string)
当在一个类型上定义一个方法时,接收器(在上面的例子中是 s)的行为就像它是方法的一个参数一样。其相当于:
func SetName1(s MyStruct, name string){
u.Name = name
}
func SetName2(s *MyStruct,name string){
u.Name = name
}
因此结构体方法是要将接收器定义成值,还是指针。这本质上与函数参数应该是值还是指针是同一个问题。
如何选择
整体有以下几个考虑因素,按重要程度顺序排列:
在使用上的考虑:方法是否需要修改接收器?如果需要,接收器必须是一个指针。
在效率上的考虑:如果接收器很大,比如:一个大的结构,使用指针接收器会好很多。
在一致性上的考虑:如果类型的某些方法必须有指针接收器,那么其余的方法也应该有指针接收器,所以无论类型如何使用,方法集都是一致的。
回到上面的例子中,从功能使用角度来看:
SetName2SetName1
另外对于基本类型、切片和小结构等类型,值接收器是非常廉价的。
因此除非方法的语义需要指针,那么值接收器是最高效和清晰的。在 GC 方面,也不需要过度关注。出现时再解决就好了。