Examples of the differences between make and new in golang are explained in detail

  • 2020-06-07 04:40:46
  • OfStack

preface

This paper mainly introduces the differences between make and new in golang, and shares them for your reference and study. Let's start with a detailed introduction:

Both new and make can be used to allocate space and initialize types, but they do differ.

new(T) returns a pointer to T

new(T) allocates a space for a new value of T type and initializes this space to the zero of T, returning the address of the new value as a pointer of T type *T, which points to the newly allocated zero of T.


p1 := new(int)
fmt.Printf("p1 --> %#v \n ", p1) //(*int)(0xc42000e250) 
fmt.Printf("p1 point to --> %#v \n ", *p1) //0
var p2 *int
i := 0
p2 = &i
fmt.Printf("p2 --> %#v \n ", p2) //(*int)(0xc42000e278) 
fmt.Printf("p2 point to --> %#v \n ", *p2) //0

The above code is equivalent, new(int) Initialize the allocated space to the zero value of int, i.e., 0, and return a pointer to int, which is the same as declaring a pointer and initializing it.

make can only be used for slice,map,channel

make can only be used for slice, map and channel. make(T, args) Returns the value of T after initialization, which is not a zero of T, nor a pointer *T, but a reference to T after initialization.


var s1 []int
if s1 == nil {
 fmt.Printf("s1 is nil --> %#v \n ", s1) // []int(nil)
}
s2 := make([]int, 3)
if s2 == nil {
 fmt.Printf("s2 is nil --> %#v \n ", s2)
} else {
 fmt.Printf("s2 is not nill --> %#v \n ", s2)// []int{0, 0, 0}
}

The zero value of slice is nil. After using make, slice is an initial slice, that is, the length, capacity and bottom pointing array of slice are all initialized by make. At this time, the content of slice is filled with the zero value of type int in the form of [0, 0, 0], and map and channel are similar.


var m1 map[int]string
if m1 == nil {
 fmt.Printf("m1 is nil --> %#v \n ", m1) //map[int]string(nil)
}
m2 := make(map[int]string)
if m2 == nil {
 fmt.Printf("m2 is nil --> %#v \n ", m2)
} else {
 fmt.Printf("m2 is not nill --> %#v \n ", m2) map[int]string{} 
}
var c1 chan string
if c1 == nil {
 fmt.Printf("c1 is nil --> %#v \n ", c1) //(chan string)(nil)
}
c2 := make(chan string)
if c2 == nil {
 fmt.Printf("c2 is nil --> %#v \n ", c2)
} else {
 fmt.Printf("c2 is not nill --> %#v \n ", c2)//(chan string)(0xc420016120)
}

make(T, args) returns a reference to T

Without special declaration, go's function default is to wear parameters by value, that is, the parameter passed through the function is a copy of the value, the modification of the value inside the function does not affect the value itself, however make(T, args) The returned value can be directly modified after passing parameters through the function, that is, map, slice, channel after passing parameters through the function, the modification inside the function will affect the value outside the function.


func modifySlice(s []int) {
 s[0] = 1
}
s2 := make([]int, 3)
fmt.Printf("%#v", s2) //[]int{0, 0, 0}
modifySlice(s2)
fmt.Printf("%#v", s2) //[]int{1, 0, 0}

This suggests that make(T, args) Returns the reference type, and you can change the original value directly within the function, as well as for map and channel.


func modifyMap(m map[int]string) {
 m[0] = "string"
}
func modifyChan(c chan string) {
 c <- "string"
}
m2 := make(map[int]string)
if m2 == nil {
 fmt.Printf("m2 is nil --> %#v \n ", m2) 
} else {
 fmt.Printf("m2 is not nill --> %#v \n ", m2) //map[int]string{}
}
modifyMap(m2)
fmt.Printf("m2 is not nill --> %#v \n ", m2) // map[int]string{0:"string"}
c2 := make(chan string)
if c2 == nil {
 fmt.Printf("c2 is nil --> %#v \n ", c2)
} else {
 fmt.Printf("c2 is not nill --> %#v \n ", c2)
}
go modifyChan(c2)
fmt.Printf("c2 is not nill --> %#v ", <-c2) //"string"

new is rarely needed


type Foo struct {
 name string
 age int
}
var foo1 Foo
fmt.Printf("foo1 --> %#v\n ", foo1) //main.Foo{age:0, name:""}
foo1.age = 1
fmt.Println(foo1.age)
foo2 := Foo{}
fmt.Printf("foo2 --> %#v\n ", foo2) //main.Foo{age:0, name:""}
foo2.age = 2
fmt.Println(foo2.age)
foo3 := &Foo{}
fmt.Printf("foo3 --> %#v\n ", foo3) //&main.Foo{age:0, name:""}
foo3.age = 3
fmt.Println(foo3.age)
foo4 := new(Foo)
fmt.Printf("foo4 --> %#v\n ", foo4) //&main.Foo{age:0, name:""}
foo4.age = 4
fmt.Println(foo4.age)
var foo5 *Foo = new(Foo)
fmt.Printf("foo5 --> %#v\n ", foo5) //&main.Foo{age:0, name:""}
foo5.age = 5
fmt.Println(foo5.age)

foo1 and foo2 are of the same type, both of which are values of Foo type. foo1 is declared by var, filed of Foo is automatically initialized to the zero value of each type, and foo2 is initialized literally.

foo3, foo4, and foo5 are one type and are all Pointers to Foo *Foo.

But all foo can use filed of Foo directly, read or modify, why?

If x is addressable, & The filed collection of x contains m, ES120en. m and ( & x).m is equivalent, go does the conversion automatically, that is, foo1.age and foo3.ES129en call is equivalent, go does the conversion automatically below.

Therefore, you can create objects directly using struct literal, which is exactly the same as new without using new.

summary

new(T) returns the pointer to T *T and points to the zero value of T.

make(T) returns the initialized T for slice, map, channel only.

conclusion


Related articles: