ChatGPT解决这个技术问题 Extra ChatGPT

Why would I make() or new()?

go

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?


a
artm

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)

make(Point)  // Illegal
make(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.


@TorstenBronger I find new to be easier to read and shows that that is the instance where the int is created.
Did you mean to write make(Point) and make(int) in those last 2 lines?
yes they did, so I fixed it.
E
Evan Shaw

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
}

It is true that 'new' cannot be used to create channels. However, in my opinion, the point is: what would happen if 'new' and 'make' are joined into a single built-in function? Certainly, such a replacement would be possible. Since it is possible, the question is: what are the objective reasons for having 2 built-in functions rather than just 1 generalized built-in function. - Your answer correctly says that 'new' cannot be used to create channels/maps/slices, but it does not provide justifications as to why Go has 'new' and 'make', instead of having 1 generalized alloc+init function.
They could be combined and it was even proposed by Rob Pike at one point: groups.google.com/d/topic/golang-nuts/kWXYU95XN04/discussion. Ultimately it didn't go through for reasons similar to the ones given in your answer.
Effective go makes the point that new returns a zeroed value, whereas map allocates non-zeroed types map, slice or channel. See golang.org/doc/effective_go.html#allocation_new
What about m := map[string]int{} instead of m := make(map[string]int)? no need to preallocate size as well.
S
Shashank Vivek

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

In new([]int), it just allocates memory for []int, but not initializing so it just returns nil; not the pointer to the memory because it is unusable. make([]int) allocates and initialize so it's usable, then return its address.
L
Loris

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\n\n", *aPtr) fmt.Println("-- COMPOSITE LITERAL --") b := []int{} bPtr := &b fmt.Println("pointer == nil :", *bPtr == nil) fmt.Printf("pointer value: %p\n\n", *bPtr) fmt.Println("-- NEW --") cPtr := new([]int) fmt.Println("pointer == nil :", *cPtr == nil) fmt.Printf("pointer value: %p\n\n", *cPtr) fmt.Println("-- VAR (not initialized) --") var d []int dPtr := &d fmt.Println("pointer == nil :", *dPtr == nil) fmt.Printf("pointer value: %p\n", *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


c
cod3rboy

There are already a lot of good answers but let me explain the need for new() and make() as separate allocators.

new(T) allocates uninitialized zeroed memory of the given type T and returns a pointer to that memory so that it is ready to use. Zeroed out just means that the allocated memory will have zero value of given type. Zero values of some go types are - int - 0 bool - false float - 0 string - "" struct - Zero value of each member

Problem with new() arises when it needs to handle three other composite types - chan, slice and map. These three types are special in essence that their underlying type is not just an another type but rather a state that needs to be initialized. For example , the underlying state of a slice consists of a pointer to the first element of internal array storage, a length that determines number of elements that can be accessed and a capacity that increases as the number of elements grow. new() certainly cannot handle allocation of such types due to their need for extra initialization step, that is where make() come into play.

make(T, args) is specially made for chan, slice and map types. It not only allocates the internal storage type of the chan, slice and map but also initializes their underlying state to make them ready to use. For example, for a slice it allocates the internal array storage, set the pointer to refer to first element in that array and set the length and capacity values.


Z
Zombo

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.


L
Lily Ballard

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.


So new should be avoided and just prefer the use of Struct syntax
j
jimt

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.


The claim that &T{} explicitly performs a heap allocation is AFAIK not based on anything in the specs. Actually I believe escape analysis are already keeping such *T on stack whenever possible in the exactly same way as with new(T).
B
Bhojendra Rauniyar

Difference between new() and make():

new(T) allocates zeroed storage for a new item of type T and returns its address, a value of type *T: it returns a pointer to a newly allocated zero value of type T, ready for use; it applies to value types like arrays and structs; it is equivalent to &T{ }

make(T) returns an initialized value of type T; it applies only to the 3 built-in reference types: slices, maps and channels.

In other words, new allocates; make initializes;

https://i.stack.imgur.com/PHIBg.jpg

var p *[]int = new([]int)
or
// *p == nil; with len and cap 0
p := new([]int)

which is only rarely useful.

https://i.stack.imgur.com/MUiI3.jpg

p := make([]int, 0)

our slice is initialized, but here points to an empty array.

Both these statements aren't very useful, the following is:

var v []int = make([]int, 10, 50)
// Or
v := make([]int, 10, 50)

This allocates an array of 50 ints and then creates a slice v with length 10 and capacity 50 pointing to the first 10 elements of the array.

Find out some rules for make() and new():

For slices, maps and channels: use make

For arrays, structs and all value types: use new

package main
type Foo map[string]string
type Bar struct {
         s string
         i int
}
func main() {
         // OK:
         y := new(Bar)
         (*y).s = "hello"
         (*y).i = 1

         // NOT OK:
         z := make(Bar) // compile error: cannot make type Bar
         z.s = "hello"
         z.i = 1

         // OK:
         x := make(Foo)
         x["x"] = "goodbye"
         x["y"] = "world"

         // NOT OK:
         u := new(Foo)
         (*u)["x"] = "goodbye" // !!panic!!: runtime error: 
                   // assignment to entry in nil map
         (*u)["y"] = "world"
}

Channel:

func main() {
    // OK:
    ch := make(chan string)
    go sendData(ch)
    go getData(ch)
    time.Sleep(1e9)

    // NOT OK:
    ch := new(chan string)
    go sendData(ch) // cannot use ch (variable of type *chan string) 
                   // as chan string value in argument to sendData
    go getData(ch)
    time.Sleep(1e9)
}

func sendData(ch chan string) {
    ch <- "Washington"
    ch <- "Tripoli"
    ch <- "London"
    ch <- "Beijing"
    ch <- "Tokio"
}

func getData(ch chan string) {
    var input string
    for {
        input = <-ch
        fmt.Printf("%s ", input)

    }
}

J
JRogerC

The benefits of "make" are heavily covered in other answers, but "New" has an added bonus over make not mentioned above: generics (as of 1.18).

Let's say you have a set of flat (all fields are primitives) structs, like the following:

type SomeStruct struct {
    V1 string `json:"v1"`
    V2 string `json:"v2"`
}

and you want to create a mapping function that turns a map[string]string into any struct. Then you could write:

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
}

but, this code will throw an error, with regards to the line myStr := T{}, about an invalid composite value. Replacing it with myStr := make(T) will through another error about no underlying type. So, you'll to replace the line with myStr := new(T) which will create a reference to a zeroed value instance of the struct.

As can be seen, when dealing with generics, new can be used to instantiate a type that is unknown at compile time.

As a side, you could have also used named return types in this specific example, but the more general usage still stands.