我为什么要make()或new()?

问题描述:

The introduction documents dedicate many paragraphs to explaining the difference between new() and make(), but in practice, you can create objects within local scope and return them.

Why would you use the pair of allocators?

简介文档专门介绍了许多段落来解释 new() code>和 make() code>,但是实际上,您可以在本地范围内创建对象并返回它们。 p>

为什么要使用一对分配器? p> DIV>

Things you can do with make that you can't do any other way:

  • Create a channel
  • Create a map with space preallocated
  • Create a slice with space preallocated or with len != cap

It's a little harder to justify new. The main thing it makes easier is creating pointers to non-composite types. The two functions below are equivalent. One's just a little more concise:

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

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

You need make() to create channels and maps (and slices, but those can be created from arrays too). There's no alternative way to make those, so you can't remove make() from your lexicon.

As for new(), I don't know of any reason offhand why you need it when you can use struct syntax. It does have a unique semantic meaning though, which is "create and return a struct with all fields initialized to their zero value", which can be useful.

Apart from everything explained in Effective Go, The main difference between new(T) and &T{} is that the latter explicitly performs a heap allocation. However it should be noted that this is implementation dependent and thus may be subject to change.

Comparing make to new makes little sense as the two perform entirely different functions. But this is explained in detail in the linked article.

Go has multiple ways of memory allocation and value initialization:

&T{...}, &someLocalVar, new, make

Allocation can also happen when creating composite literals.


new can be used to allocate values such as integers, &int is illegal:

new(Point)
&Point{}      // OK
&Point{2, 3}  // Combines allocation and initialization

new(int)
&int          // Illegal

// Works, but it is less convenient to write than new(int)
var i int
&i

The difference between new and make can be seen by looking at the following example:

p := new(chan int)   // p has type: *chan int
c := make(chan int)  // c has type: chan int

Suppose Go does not have new and make, but it has the built-in function NEW. Then the example code would look like this:

p := NEW(*chan int)  // * is mandatory
c := NEW(chan int)

The * would be mandatory, so:

new(int)        -->  NEW(*int)
new(Point)      -->  NEW(*Point)
new(chan int)   -->  NEW(*chan int)
make([]int, 10) -->  NEW([]int, 10)

new(Point)  // Illegal
new(int)    // Illegal

Yes, merging new and make into a single built-in function is possible. However, it is probable that a single built-in function would lead to more confusion among new Go programmers than having two built-in functions.

Considering all of the above points, it appears more appropriate for new and make to remain separate.

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.

The following examples from Effective Go make it very clear:

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

new(T) : it returns a pointer to type T a value of type *T, it allocates and zeroes the memory. new(T) is equivalent to &T{}.

make(T) : it returns an initialized value of type T, It allocates and initializes the memory. Its used for slices, map and channels.

  • new(T) - Allocates memory, and sets it to the zero value for type T..
    ..that is 0 for int, "" for string and nil for referenced types (slice, map, chan)

    Note that referenced types are just pointers to some underlying data structures, which won't be created by new(T)
    Example: in case of slice, the underlying array won't be created, thus new([]int) returns a pointer to nothing

  • make(T) - Allocates memory for referenced data types (slice, map, chan), plus initializes their underlying data structures

    Example: in case of slice, the underlying array will be created with the specified length and capacity
    Bear in mind that, unlike C, an array is a primitive type in Go!


That being said:

make(T) behaves like composite-literal syntax new(T) behaves like var (when the variable is not initialized)
func main() {
    fmt.Println("-- MAKE --")
    a := make([]int, 0)
    aPtr := &a
    fmt.Println("pointer == nil :", *aPtr == nil)
    fmt.Printf("pointer value: %p

", *aPtr)

    fmt.Println("-- COMPOSITE LITERAL --")
    b := []int{}
    bPtr := &b
    fmt.Println("pointer == nil :", *bPtr == nil)
    fmt.Printf("pointer value: %p

", *bPtr)

    fmt.Println("-- NEW --")
    cPtr := new([]int)
    fmt.Println("pointer == nil :", *cPtr == nil)
    fmt.Printf("pointer value: %p

", *cPtr)

    fmt.Println("-- VAR (not initialized) --")
    var d []int
    dPtr := &d
    fmt.Println("pointer == nil :", *dPtr == nil)
    fmt.Printf("pointer value: %p
", *dPtr)
}

Run the program

-- MAKE --
pointer == nil : false
pointer value: 0x118eff0  # address to underlying array

-- COMPOSITE LITERAL --
pointer == nil : false
pointer value: 0x118eff0  # address to underlying array

-- NEW --
pointer == nil : true
pointer value: 0x0

-- VAR (not initialized) --
pointer == nil : true
pointer value: 0x0

Further reading:
https://golang.org/doc/effective_go.html#allocation_new https://golang.org/doc/effective_go.html#allocation_make