You must be familiar with new and make, but when to use new and when to use make, perhaps many new developers do not understand, this article will briefly note the differences between new and make and the timing of their use.

Using the new keyword

Go provides two ways to allocate memory, one is new and the other is make. These two keywords do different things and have different types of applications, which may cause some confusion for those who are just starting out, but the rules for using these two keywords are very simple. new(T) declares that it will directly get the storage location and configure Zero Value (initialization), that is, the numeric type is 0 and the string type is "". At the bottom is the example program.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package main

import "fmt"

func main() {
  foo := new(int)
  fmt.Println(foo)
  fmt.Println(*foo)
  fmt.Printf("%#v", foo)
}

After running, you can see the following result.

1
2
3
4
$ go run main.go 
0xc00001a110
0
(*int)(0xc00001a110)

The above practice is less used, more people use it on struct, because of the feature of new, it can be used directly on struct to do initialization, here is the sample program.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
  "bytes"
  "fmt"
  "sync"
)

type SyncedBuffer struct {
  lock   sync.Mutex
  buffer bytes.Buffer
  foo    int
  bar    string
}

func main() {
  p := new(SyncedBuffer)
  fmt.Println("foo:", p.foo)
  fmt.Println("bar:", p.bar)
  fmt.Printf("%#v\n", p)
}

As you can see above, initialization is quickly achieved through new, but one inconvenience is that if the developer wants to plug in a specific initialization value, there is no way to do it through new, so most of the writing will be changed to the following, example link.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
  "bytes"
  "fmt"
  "sync"
)

type SyncedBuffer struct {
  lock   sync.Mutex
  buffer bytes.Buffer
  foo    int
  bar    string
}

func main() {
  p := &SyncedBuffer{
    foo: 100,
    bar: "foobar",
  }
  fmt.Println("foo:", p.foo)
  fmt.Println("bar:", p.bar)
  fmt.Printf("%#v\n", p)
}

Or most of them will write a new Func to do the initialization settings, example program as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
  "bytes"
  "fmt"
  "sync"
)

type SyncedBuffer struct {
  lock   sync.Mutex
  buffer bytes.Buffer
  foo    int
  bar    string
}

func NewSynced(foo int, bar string) *SyncedBuffer {
  return &SyncedBuffer{
    foo: foo,
    bar: bar,
  }
}

func main() {
  p := NewSynced(100, "foobar")
  fmt.Println("foo:", p.foo)
  fmt.Println("bar:", p.bar)
  fmt.Printf("%#v\n", p)
}

However, if new is used on slice, map and channel, its initial value will be nil, see the following example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import (
  "fmt"
)

func main() {
  p := new(map[string]string)
  test := *p
  test["foo"] = "bar"
  fmt.Println(test)
}

The result is panic.

1
2
3
4
5
6
7
$ go run main.go 
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /app/main.go:10 +0x4f
exit status 2

The initialization of map will result in a nil, so usually when declaring slice, map and channel, another declaration method provided by Go, make, is used.

Using make keywords

The difference between make and new is that new returns a pointer, while make does not. make is usually only used to declare three places, slice, map and channel, if you really want to get a pointer, it is recommended to use new. The following is map as an example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

func main() {
  var p *map[string]string
  // new
  p = new(map[string]string)
  *p = map[string]string{
    "bar": "foo",
  }
  people := *p
  people["foo"] = "bar"

  fmt.Println(people)
  fmt.Println(p)

  // make
  foobar := make(map[string]string)
  foobar["foo"] = "bar"
  foobar["bar"] = "foo"
  fmt.Println(foobar)
}

The above example shows that p is declared as a map pointer, and after new initializes the map, it needs to be written as map[string]string{} independently to work properly. Usually, I rarely use new in my own development, instead, I use make when declaring slice, map and channel. Remember, make will not return a pointer, so if you really want to get a pointer, use new, but the code will be more complicated.

Summary

To summarize the difference between make and new

  • make can allocate and initialize the required memory space and structure, while new can only return the pointer location.
  • make can only be used in three types slice , map and channel
  • make can initialize the length and capacity of the above three formats to provide efficiency and reduce overhead