为了账号安全,请及时绑定邮箱和手机立即绑定

在 Go 中实例化类型的首选方法

在 Go 中实例化类型的首选方法

Go
狐的传说 2021-06-18 14:27:33
我喜欢 Go 没有给我一百万种方法来做简单的事情——借用 Python 之禅,“应该有一种——最好只有一种——明显的方法来做这件事。”但是,我不清楚实例化类型的首选/惯用方式。基本类型很简单:n := 0t := 1.5str := "Hello"但是结构呢?以下是否等价,如果是,哪个更受欢迎,为什么?var f Foo    f := Foo{}切片呢?我可以做var xs []int, xs := []int{}, or xs := make([]int),但我认为第一个选项(与结构相反)与其他选项不同?我认为这也适用于地图。有了指针,我听说new应该避免这种情况。这是一个很好的建议,如果是这样,什么可以算作 的有效用法new?我意识到这在一定程度上可能是风格问题,但在任何情况下,偏好特定风格的理由都会有所帮助。
查看完整描述

3 回答

?
元芳怎么了

TA贡献1798条经验 获得超7个赞

当你声明一个变量时, whereT是某种类型:


var name T

Go 给你一块未初始化的“归零”内存。


对于原语,这意味着var name int它将是 0,并且var name string将是“”。在C 中,它可能被归零,或者可能是出乎意料的。Go 保证未初始化的变量是该类型的零等价物。


在内部,切片、映射和通道被视为指针。指针零值为零,意味着它指向零内存。如果没有初始化它,如果您尝试对其进行操作,您可能会遇到恐慌。


该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:使用 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{}在这种情况下更干净,尽管我倾向于使用带有结构的指针来避免在传递时进行复制。


另一件要记住的事情是, a[]*struct比 更小,调整大小更便宜[]struct,假设结构比指针大得多,指针通常为 4 - 8 个字节(64 位上为 8 个字节?)。


查看完整回答
反对 回复 2021-06-21
?
慕斯王

TA贡献1864条经验 获得超2个赞

您可以查看 Go 标准库源,在那里您可以找到许多惯用的 Go 代码。

您是对的:var xs []int与其他两个变体不同,因为它不“初始化”xs,xs 为零。而另外两个确实构造了一个切片。xs := []int{}如果您需要一个零上限的空切片,则很常见,同时make为您提供更多选择:长度和容量。另一方面,通常从一个 nil 切片开始并通过附加来填充var s []int; for ... { s = append(s, num) }

new完全无法避免,因为它是创建指针的唯一方法,例如指向 uint32 或其他内置类型。但你是对的,写作a := new(A)是非常罕见的,主要是a := &A{}因为这可以变成a := &A{n: 17, whatever: "foo"}. 使用 ofnew并不是真的不鼓励,但考虑到结构文字的能力,对我来说它只是 Java 的遗留物。


查看完整回答
反对 回复 2021-06-21
  • 3 回答
  • 0 关注
  • 255 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信