Types 在Go中实例化类型的首选方法
我喜欢这样一个事实,Go没有给我一百万种做简单事情的方法——借用Python的禅宗,“应该有一种——最好只有一种——显而易见的方法来做。” 但是,我不清楚实例化类型的首选/惯用方式。基本类型很简单:Types 在Go中实例化类型的首选方法,types,go,declaration,instantiation,idioms,Types,Go,Declaration,Instantiation,Idioms,我喜欢这样一个事实,Go没有给我一百万种做简单事情的方法——借用Python的禅宗,“应该有一种——最好只有一种——显而易见的方法来做。” 但是,我不清楚实例化类型的首选/惯用方式。基本类型很简单: n := 0 t := 1.5 str := "Hello" 但是结构呢?以下是否等效,如果是,首选哪一种,为什么 var f Foo f := Foo{} 切片怎么样?我可以做var xs[]int,xs:=[]int{},或者xs:=make([]int),但是我认为第一个选项(与结
n := 0
t := 1.5
str := "Hello"
但是结构呢?以下是否等效,如果是,首选哪一种,为什么
var f Foo
f := Foo{}
切片怎么样?我可以做var xs[]int
,xs:=[]int{}
,或者xs:=make([]int)
,但是我认为第一个选项(与结构相反)与其他选项不同?我想这也适用于地图
对于指针,我听说应该避免使用new
。这是一个好建议吗?如果是的话,什么才算是new
的有效用法
我意识到这在一定程度上可能是一个风格的问题,但在任何情况下,选择特定风格的理由都是有帮助的。您可以查看Go标准库的源代码,在那里可以找到许多惯用的Go代码 您是对的:
var xs[]int
不同于其他两个变量,因为它不“初始化”xs,xs为nil。而另外两个则真正构成了一个切片xs:=[]int{}
如果您需要一个具有零上限的空片,那么它很常见,而make
提供了更多选项:长度和容量。另一方面,通常以nil片开始,然后通过追加var s[]int来填充;对于{s=append(s,num)}
new
无法避免,因为这是创建指针(例如指向uint32或其他内置类型)的唯一方法。但是你是对的,写a:=new(a)
是非常不常见的,而且大部分写为a:=&a{}
,因为这可以变成a:=&a{n:17,不管什么:“foo”}
。使用new
并不是真的不受欢迎,但考虑到struct文本的能力,我觉得它只是Java的一个遗留问题 在Google IO上与围棋小组在炉边聊天时,观众中有人问围棋小组他们想从语言中获得什么
Rob说他希望有更少的方法来声明变量,并提到:
冒号等于overwrite,命名为result parameters(),在for循环中重用的变量容易混淆,尤其是对于闭包。但是,语言可能不会有太大变化。当您声明变量时,其中
T
是某种类型:
var name T
Go为您提供一段未初始化的“零”内存
对于原语,这意味着var name int
将为0,var name string
将为“”。在里面Go保证未初始化的变量是该类型的零等效变量
内部切片、贴图和通道被视为指针。指针的零值是nil,这意味着它指向nil内存。如果不初始化它,如果您尝试对其进行操作,可能会遇到恐慌
make
功能是专门为切片、贴图或频道设计的。make函数的参数为:
make(T type, length int[, capacity int]) // For slices.
make(T[, capacity int]) // For a map.
make(T[, bufferSize int]) // For a channel. How many items can you take without blocking?
A sliceslength
是它以多少项开始。容量是在需要调整大小之前分配的内存(内部,新大小*2,然后复制)。有关更多信息,请参阅
结构:new(T)
相当于&T{}
,而不是T{}
*new(T)
相当于*&T{}
切片:make([]T,0)
相当于[]T{}
映射:make(map[T]T)
相当于map[T]T{}
至于首选哪种方法,我问自己以下问题:
我现在知道函数中的值吗
如果答案是“是”,那么我选择上面的一个T{…}
。如果答案是“否”,那么我使用make或new
例如,我会避免这样的事情:
type Name struct {
FirstName string
LastName string
}
func main() {
name := &Name{FirstName:"John"}
// other code...
name.LastName = "Doe"
}
func main() {
name := new(Name)
name.FirstName = "John"
// other code...
name.LastName = "Doe"
}
相反,我会这样做:
type Name struct {
FirstName string
LastName string
}
func main() {
name := &Name{FirstName:"John"}
// other code...
name.LastName = "Doe"
}
func main() {
name := new(Name)
name.FirstName = "John"
// other code...
name.LastName = "Doe"
}
为什么??因为通过使用new(Name)
我清楚地表明我打算稍后填充这些值。如果我使用了&Name{…}
,就不清楚我是否打算稍后在同一个函数中添加/更改一个值,而不读取代码的其余部分
当您不需要指针时,结构例外。我将使用T{}
,但如果我计划添加/更改值,我不会在其中添加任何内容。当然,*new(T)
也可以,但这就像使用*&T{}
一样<在这种情况下,code>T{}更干净,尽管我倾向于使用带结构的指针,以避免在传递它时生成副本
另一件需要记住的事情是,[]*struct
比[]struct
更小,调整大小也更便宜,假设struct比指针大得多,指针通常是4-8字节(64位8字节?)
var xs[]int
xs:=[]int{}
xs:=make([]int,2)
xs := make([]int, 2)
xs[1] = 100
我避免使用第二项,除非我的值包括:
xs := []int{9, 8}
xs := map[string]int{"month": 12, "day": 31}
f := Foo{31}
f := Foo{Day: 31}
f := &Foo{31}
f := &Foo{Day: 31}
地图
xs:=make(映射[string]int)
xs:=map[string]int{}
xs := []int{9, 8}
xs := map[string]int{"month": 12, "day": 31}
f := Foo{31}
f := Foo{Day: 31}
f := &Foo{31}
f := &Foo{Day: 31}
结构
var f Foo
f:=Foo{}
xs := []int{9, 8}
xs := map[string]int{"month": 12, "day": 31}
f := Foo{31}
f := Foo{Day: 31}
f := &Foo{31}
f := &Foo{Day: 31}
指针
var f Foo&f
f:=new(Foo)
f:=&Foo{}
xs := []int{9, 8}
xs := map[string]int{"month": 12, "day": 31}
f := Foo{31}
f := Foo{Day: 31}
f := &Foo{31}
f := &Foo{Day: 31}
我避免使用第二项,除非变量的每次使用都处于“指针模式”:
+我并没有真正回答这个问题,但我确实同意——很高兴知道派克自己这么说。这是我在GoLang身上发现的一个弱点:有太多的方法来声明,却不清楚它们各自的优缺点和适用性——有时给我一种“还没完成”的感觉。