我喜欢Go没有给予我一百万种方法来做简单的事情的事实-借用Python的禅宗,“应该有一个-最好只有一个-明显的方法来做。
然而,我不清楚示例化类型的首选/惯用方法。基本类型很简单:
n := 0
t := 1.5
str := "Hello"
但是结构体呢?下列各项是否等同,如果是,哪一项是首选,为什么?
var f Foo
f := Foo{}
切片呢?我可以使用var xs []int
、xs := []int{}
或xs := make([]int)
,但我认为第一个选项(与结构体相反)与其他选项不同?我想这也适用于Map。
对于指针,我听说应该避免使用new
。这是一个好的建议吗?如果是的话,new
的有效用法是什么?
我意识到这可能部分是一个风格问题,但在任何情况下,偏好某种特定风格的理由都会有所帮助。
5条答案
按热度按时间ni65a41a1#
当你声明一个变量时,其中
T
是某种类型:Go会给你一段未初始化的“归零”内存。
对于基元,这意味着
var name int
将为0,var name string
将为“"。在C it might be zeroed, or might be something unexpected中。Go保证未初始化的变量是类型的零等价物。在内部,切片、贴图和通道被视为指针。指针零值为nil,意味着它指向nil内存。如果不初始化它,如果尝试对其进行操作,可能会遇到死机。
make
函数专为切片、贴图或通道而设计。make函数的参数是:一个slices
length
是它开始的项目数。容量是在需要调整大小之前分配的内存(内部,新大小 * 2,然后复制)。有关更多信息,请参阅Effective Go:使用make分配。new(T)
等价于&T{}
,而不是T{}
。*new(T)
相当于*&T{}
。切片:
make([]T,0)
相当于[]T{}
。Map:
make(map[T]T)
相当于map[T]T{}
。至于哪种方法更好,我问自己以下问题:
我现在知道函数内部的值吗?
如果答案是“是”,那么我选择上面的
T{...}
。如果答案是“否”,那么我使用make或new。例如,我会避免这样的事情:
相反,我会这样做:
为什么?因为通过使用
new(Name)
,我清楚地表明我打算稍后填充这些值。如果我使用&Name{...}
,那么在没有阅读其余代码的情况下,我将无法清楚地在同一函数中添加/更改值。当你不需要指针的时候,结构体是个例外。我将使用
T{}
,但如果我打算添加/更改值,我不会在其中放入任何内容。当然,*new(T)
也可以工作,但这就像使用*&T{}
一样。在这种情况下,T{}
更简洁,尽管我倾向于在结构中使用指针,以避免在传递它时进行复制。另一件需要记住的事情是,
[]*struct
比[]struct
更小,调整大小更便宜,假设结构体比指针大得多,通常为4 - 8字节(64位上为8字节?).ut6juiuv2#
在Google IO与Go团队的炉边谈话中,观众中有人问Go团队他们想从语言中提取什么。
Rob说他希望有更少的方法来声明变量,并提到:
冒号等于覆盖,命名结果参数(https://plus.google.com/+AndrewGerrand/posts/LmnDfgehorU),在for循环中重用的变量令人困惑,特别是对于闭包。但是,语言可能不会有太大的变化。
os8fio9y3#
你可以看看Go标准库的源代码,在那里你可以找到很多惯用的Go代码。
你说得对:
var xs []int
与其他两个变量不同,因为它不“初始化”xs,xs为nil。而另外两个实际上构建了一个切片。xs := []int{}
是常见的,如果你需要一个零上限的空切片,而make
给你更多的选择:长度和容量。另一方面,通常以nil切片开始,并通过追加来填充,如var s []int; for ... { s = append(s, num) }
。new
不能避免total,因为它是创建指针的唯一方法,例如uint32或其他内置类型。但你是对的,写a := new(A)
是非常罕见的,大多数情况下写为a := &A{}
,因为这可以转换为a := &A{n: 17, whatever: "foo"}
。new
的使用并不真正被禁止,但考虑到结构体文字的能力,它在我看来就像是Java的残余。9wbgstp74#
slice
var xs []int
xs := []int{}
xs := make([]int, 2)
我避免第三项,除非我需要声明一个大小:
我避免第二项,除非我有值包括:
Map
xs := make(map[string]int)
xs := map[string]int{}
我避免第二项,除非我有值包括:
struct
var f Foo
f := Foo{}
我避免第二项,除非我有值包括:
指针
var f Foo; &f
f := new(Foo)
f := &Foo{}
我避免第三项,除非我有值包括:
我避免第二项,除非每次使用变量都是在“指针模式”下:
olhwl3o25#
在这个线程中有很多对术语 initialized 的误用。保证为nil、0或“”的值都是 initialized,而不是uninitialized。可能具有随机剩余内存内容的值未初始化。C不会初始化内存,Go * 会。