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

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


当前回答

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

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

其他回答

“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可以用来实例化编译时未知的类型。

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

你能用它做的事情用其他方法做不到:

创建通道 创建一个预分配空间的映射 创建一个预先分配空间的切片,或者使用len != cap

要证明新的合理性有点难。它简化的主要事情是创建指向非复合类型的指针。 下面两个函数是等价的。一个更简洁一点:

func newInt1() *int { return new(int) }

func newInt2() *int {
    var i int
    return &i
}

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

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

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

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

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

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