介绍文档用了很多段落来解释new()和make()之间的区别,但实际上,您可以在局部范围内创建对象并返回它们。

为什么要使用这对分配器?


当前回答

new(T):它返回一个指向类型T的指针,一个类型为*T的值,它分配并归零内存。new(T)等价于&T{}。

make(T):返回类型为T的初始化值,分配并初始化内存。它用于切片,映射和通道。

其他回答

您需要make()来创建通道和映射(以及切片,但这些也可以从数组创建)。没有其他方法来创建这些,所以不能从词典中删除make()。

至于new(),当可以使用结构语法时,我不知道为什么还需要它。但它确实有一个独特的语义含义,即“创建并返回一个将所有字段初始化为零值的结构体”,这很有用。

make function allocates and initializes an object of type slice, map, or chan only. Like new, the first argument is a type. But, it can also take a second argument, the size. Unlike new, make’s return type is the same as the type of its argument, not a pointer to it. And the allocated value is initialized (not set to zero value like in new). The reason is that slice, map and chan are data structures. They need to be initialized, otherwise they won't be usable. This is the reason new() and make() need to be different.

下面是Effective Go的例子:

p *[]int = new([]int) // *p = nil, which makes p useless
v []int = make([]int, 100) // creates v structure that has pointer to an array, length field, and capacity field. So, v is immediately usable

除了在Effective Go中解释的所有内容外,new(T)和&T{}之间的主要区别是后者显式执行堆分配。但是,应该注意的是,这取决于实现,因此可能会发生变化。

比较make和new没有什么意义,因为两者执行完全不同的功能。但这在链接的文章中有详细的解释。

已经有很多很好的答案,但是让我解释一下new()和make()作为单独的分配器的必要性。

new(T)分配给定类型T的未初始化的零内存,并返回指向该内存的指针,以便它可以使用。归零仅仅意味着分配的内存中给定类型的值为零。某些go类型的零值为- Int - 0 Bool - false Float - 0 字符串- "" struct -每个成员的零值

Problem with new() arises when it needs to handle three other composite types - chan, slice and map. These three types are special in essence that their underlying type is not just an another type but rather a state that needs to be initialized. For example , the underlying state of a slice consists of a pointer to the first element of internal array storage, a length that determines number of elements that can be accessed and a capacity that increases as the number of elements grow. new() certainly cannot handle allocation of such types due to their need for extra initialization step, that is where make() come into play.

make(T, args)是专门为chan, slice和map类型创建的。它不仅分配chan、slice和map的内部存储类型,而且还初始化它们的底层状态,使它们可以使用。例如,对于片,它分配内部数组存储,设置指针指向该数组中的第一个元素,并设置长度和容量值。

“make”的好处在其他答案中有很多,但是“New”比上面没有提到的make有一个额外的好处:泛型(截至1.18)。

假设你有一组平面(所有字段都是原语)结构体,如下所示:

type SomeStruct struct {
    V1 string `json:"v1"`
    V2 string `json:"v2"`
}

你想要创建一个映射函数,将一个map[string]字符串转换为任何结构体。然后你可以这样写:

func GetStructFromMap[T any](values map[string]string) (T, error) {
    myStr := T{}
    bytes, err := json.Marshal(values)
    if err != nil {
        return *myStr, err
    }

    if err := json.Unmarshal(bytes, str); err != nil {
        return *myStr, err
    }

    return *myStr, nil
}

但是,这段代码将抛出一个关于myStr:= T{}行的错误,关于无效的组合值。用myStr:= make(T)替换它会产生另一个关于没有底层类型的错误。因此,您需要将该行替换为myStr:= new(T),这将创建一个对该结构的零值实例的引用。

可以看到,在处理泛型时,new可以用来实例化编译时未知的类型。

另一方面,在这个特定的示例中还可以使用命名返回类型,但更普遍的用法仍然有效。