当你声明一个变量时,T 是某种类型:
var name T
Go 为您提供了一块未初始化的“归零”内存。
对于原语,这意味着var name int 将是 0,而var name string 将是“”。在C it might be zeroed, or might be something unexpected。 Go 保证未初始化的变量是类型的零等价物。
内部切片、映射和通道被视为指针。指针零值为零,这意味着它指向零内存。不初始化的话,尝试操作可能会遇到panic。
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?
切片length 是它开始的项目数。容量是需要调整大小之前分配的内存(内部,新大小 * 2,然后复制)。如需更多信息,请参阅Effective Go: Allocation with make。
结构: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"
}
为什么?因为通过使用new(Name),我明确表示我打算稍后填充这些值。如果我使用&Name{...},则不清楚我打算稍后在同一个函数中添加/更改一个值而不阅读其余代码。
当您不需要指针时,结构例外。我将使用T{},但如果我打算添加/更改值,我不会在其中添加任何内容。当然*new(T) 也可以,但这就像使用*&T{}。 T{} 在这种情况下更简洁,尽管我倾向于使用带有结构的指针以避免在传递时复制。
要记住的另一件事是,[]*struct 比[]struct 更小,调整大小更便宜,假设结构比指针大得多,通常是 4 - 8 个字节(64 位上的 8 个字节?)。