Golang new VS make

  • Golang中new和make的区别?
  • 复杂结构的类型如何正确初始化

new() vs make()

需要区分在Golang中声明初始化的区别?

1
var a []int // 声明,未初始化,此时为nil slice

new()

The built-in function new takes a type T, allocates storage for a variable of that type at run time, and returns a value of type *T pointing to it.The variable is initialized as described in the section on initial values.

new()是内存分配的内置函数,传入类型T,则在运行时分配内存,返回*T类型的值(指向新分配内存地址的指针)。

变量将按照类型对应的零值来进行初始化,如map/slice零值均为nil。

make()

The built-in function make takes a type T, which must be a slice, map or channel type, optionally followed by a type-specific list of expressions. It returns a value of type T (not *T). The memory is initialized as described in the section on initial values.

内置函数make()仅支持特定类型参数(slice/map/channel),返回类型为T的变量值(注意:new返回*T类型值,make返回T类型值)。变量初始化和new保持一致的策略,均按零值处理。

  • slice
  • map
  • channel
  • 或者上述类型,再加其他参数
1
2
3
4
5
6
7
8
9
10
Call             Type T     Result

make(T, n) slice slice of type T with length n and capacity n
make(T, n, m) slice slice of type T with length n and capacity m

make(T) map map of type T
make(T, n) map map of type T with initial space for approximately n elements

make(T) channel unbuffered channel of type T
make(T, n) channel buffered channel of type T, buffer size n

复杂结构体的初始化

1
2
3
4
5
6
7
8
9
10
type Foo struct {
a int
b string
c map[int]int
d []int
}
type Bar struct {
e int
f *Foo
}

对于上面Foo类型的复合结构,初始化时要注意下,直接new Foo()的对象中map是没有分配空间的,也就是nil map,此时不能直接使用。

下面的代码,会抛出panic:panic: assignment to entry in nil map

1
2
3
4
h1 := &Foo{}
// h1 := new(Foo)和上面代码等价
h1.c[1] = 111 // panic: assignment to entry in nil map
h1.d = append(h1.d, 1) // nil slice可通过append追加元素

正确的用法:定义类型对应的NewTypeName()方法专门用于类型初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func NewFoo() *Foo {
foo := new(Foo)
foo.c = make(map[int]int)
return foo
}

func NewBar() *Bar {
bar := new(Bar)
bar.f = NewFoo()
return bar
}

func main() {
h1 := NewFoo()
h1.c[1] = 11
fmt.Println(h1)

h2 := NewBar()
h2.f.c[2] = 22 // 可以正确写入map元素
fmt.Println(*h2.f)
}

结论

  • make仅用于mapslicechannel
  • new可用于类型T的内存分配,类型各字段初始值为类型对应零值
  • 复合类型,多通用NewTypeName()自定义函数,返回新建类型变量,而类型内字段在此方法内进行有效的内存分配操作。(如map应通过make开辟空间)

参考文档