BudiBadu Logo

Samplebadu

Code with Example
BudiBadu Logo
Samplebadu

Go by Example: Make vs New

Go 1.23

Go has two primitives for allocation: `make` and `new`. This example clarifies the difference: `new` allocates zeroed memory, while `make` initializes slices, maps, and channels.

Code

package main

import "fmt"

func main() {
    // 1. Using 'new'
    // Allocates memory for an int, zeroes it, returns *int
    p := new(int)
    fmt.Println("new(int):", *p) // 0
    
    // Allocates memory for a struct, zeroes fields, returns *Struct
    type User struct { Name string; Age int }
    u := new(User)
    fmt.Printf("new(User): %+v\n", u) // &{Name:"" Age:0}

    // 2. Using 'make'
    // ONLY for slices, maps, and channels.
    // Allocates and initializes internal structure. Returns T (not *T).
    
    // Slice: allocates array, sets len=5, cap=5
    s := make([]int, 5) 
    fmt.Printf("make slice: len=%d cap=%d\n", len(s), cap(s))

    // Map: allocates hash table structure
    m := make(map[string]int)
    m["key"] = 1
    fmt.Println("make map:", m)

    // Channel: allocates channel buffer
    c := make(chan int, 2)
    fmt.Println("make chan:", c)

    // 3. Incorrect usage (Common Pitfall)
    // var m2 map[string]int
    // m2["key"] = 1 // PANIC! m2 is nil. make() was needed.
}

Explanation

The distinction between new and make in Go is a frequent source of confusion, yet it stems from a clear separation of concerns regarding memory allocation versus type initialization. The built-in new(T) function is a general-purpose memory allocator that reserves zeroed storage for a value of type T and returns a pointer (*T) to it; it does not perform any specific initialization beyond clearing the memory, making it suitable for primitive types or structs where the zero value is useful. In contrast, make(T, args) is a specialized constructor strictly for slices, maps, and channels—types that require the initialization of complex internal data structures (such as pointers to underlying arrays, hash tables, or wait queues) before they can be safely used.

Crucially, make returns an initialized value of type T (not a pointer), reflecting the fact that slices, maps, and channels are reference types that internally manage pointers to their backing data. Attempting to use new with these types would result in a pointer to a nil slice or map, which is technically valid but functionally useless for operations like assignment, leading to runtime panics. Therefore, the rule of thumb is simple: use make for the three built-in reference types to ensure they are ready for use, and use `new` (or more commonly, struct literals like &Type{}) for everything else.

  • new(T): Allocates memory, zeroes it, returns *T. Used for structs, primitives.
  • make(T): Initializes internal data structure, returns T. Only for slices, maps, channels.
  • Zero Value: new gives you a pointer to a zero value.
  • Reference Types: Slices/Maps/Chans must be initialized with make to be useful.

Code Breakdown

8
Using 'new(int)'. This asks the runtime to find 4 or 8 bytes of memory (depending on architecture), set them to 0, and give us the address. 'p' is of type '*int'.
21
Using 'make' for a slice. A slice is a header containing a pointer, length, and capacity. 'make' allocates the underlying array AND creates this header. If we used 'new([]int)', we would get a pointer to a nil slice header, which isn't what we usually want.
25
Using 'make' for a map. Maps are complex hash tables. 'make' initializes the hashing algorithm and buckets. Writing to a nil map (which you'd get from 'var m map[string]int') causes a panic.