Choosing Between make and new in Go
Daniel Hayes
Full-Stack Engineer · Leapcell

Go language provides two commonly used ways to allocate memory: make and new. Although both are used for memory allocation, their roles and usage scenarios are quite different. Understanding the differences between these two is crucial for writing efficient and maintainable Go code. This article will thoroughly analyze the differences between make and new, their suitable scenarios, and offer some usage tips.
Basic Differences Between make and new
new: Creates Zero Value of Pointer Types
new is a keyword in Go used for memory allocation. Its function is to allocate a block of memory for a type and return a pointer to that memory. The memory allocated by new is initialized to the zero value of the type.
Example: Using new
package main import "fmt" func main() { var p *int = new(int) fmt.Println(*p) // Output: 0, memory allocated for int by `new` is initialized to zero value }
- Return type:
newreturns a pointer to the type. - Zero value initialization:
newinitializes the allocated memory to the zero value of the type. For example, forinttype, the zero value is0; forstringtype, it is an empty string"".
make: Initializes Slices, Maps, and Channels
make is a special built-in function in Go, specifically used to initialize three built-in data types: slice, map, and channel. Unlike new, make does not return a pointer to the allocated memory, but instead returns the initialized object itself.
Example: Using make
package main import "fmt" func main() { // Initialize slice s := make([]int, 5) fmt.Println(s) // Output: [0 0 0 0 0] // Initialize map m := make(map[string]int) m["age"] = 30 fmt.Println(m) // Output: map[age:30] // Initialize channel ch := make(chan int, 2) ch <- 1 fmt.Println(<-ch) // Output: 1 }
- Return type:
makereturns the object itself (slice, map, or channel), not a pointer. - Memory allocation and initialization:
makenot only allocates memory but also initializes the data structure itself. For example, when initializing a slice,makeallocates the underlying array and sets its length and capacity.
Main Differences Summary
-
Purpose:
new: Allocates memory and returns a pointer to the type.make: Initializes and returns a slice, map, or channel object.
-
Return Value:
new: Returns a pointer to the type.make: Returns the initialized object itself.
-
Applicable Types:
new: All types.make: Slice, map, and channel.
-
Initialization:
new: Returns zero value.make: Initializes according to the data structure’s type.
Tips for Using make and new
Tips for Using new
Suitable for struct types:
new is often used to allocate memory for structs and return pointers to them. It is important to note that the initial value of a struct pointer is the zero value of the struct.
Example: Using new to allocate memory for a struct
type Person struct { Name string Age int } func main() { p := new(Person) fmt.Println(p) // Output: &{ 0} fmt.Println(p.Name) // Output: empty string fmt.Println(p.Age) // Output: 0 }
- Pointers created by
new: Sincenewreturns a pointer to the struct, you can directly modify its fields withp.Nameorp.Age.
Tips for Using make
Initialize slices with specified capacity:
make can be used to initialize a slice with a specified length and capacity. Using make, you can efficiently allocate the underlying array and initialize the slice.
Example: Using make to initialize a slice with capacity
// Initialize a slice with length 5 and capacity 10 s := make([]int, 5, 10) fmt.Println(len(s), cap(s)) // Output: 5 10
- Specify map capacity during initialization: When creating a map with
make, you can specify its initial capacity, which helps optimize performance by avoiding multiple memory expansions as elements are inserted.
Example: Using make to initialize a map
m := make(map[string]int, 10) // Set initial capacity to 10 m["age"] = 30 m["height"] = 175 fmt.Println(m) // Output: map[age:30 height:175]
- Initialize buffered channels: Use
maketo create a buffered channel, specifying the channel’s buffer size. This is very useful in concurrent programming.
Example: Using make to create a buffered channel
ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) // Output: 1
Choosing the Appropriate Memory Allocation Method
- Usage scenario for structs: If you only need a pointer to a struct and have no special requirements during initialization, using
newis a simple and common approach. - Usage scenario for slices, maps, and channels: If you need to initialize a slice, map, or channel and may modify their contents,
makeis the more appropriate choice—especially when you need to specify capacity in advance.
Performance Considerations of make and new
- Memory allocation overhead: When initializing slices, maps, and channels,
makenot only allocates memory but also performs type initialization, which may incur additional overhead. In contrast,newonly allocates memory and initializes it to the zero value, so its overhead is relatively small. - Avoid unnecessary memory allocations: For types such as slices, maps, or channels, it is recommended to specify an appropriate capacity when using
maketo reduce the number of memory reallocations.
Common Misuses
- Incorrectly using
newto create slices or maps: Whennewis used with slices, maps, or channels, it only returns the zero value of the type and does not perform initialization. Therefore, if you usenewto create a slice, map, or channel and try to access its contents directly, it will result in a runtime error.
Incorrect example: Misusing new to create a map
m := new(map[string]int) // Incorrect: returns a pointer, not an initialized map m["age"] = 30 // Runtime error: m is nil
Correct example: You should use make to initialize a map.
m := make(map[string]int) m["age"] = 30
Summary
In Go, make and new are both keywords for memory allocation, and although their functions are similar, they have clear differences.
new is used to allocate memory for a type and returns a pointer, and it is suitable for most types;
while make is mainly used to initialize slices, maps, and channels, providing stronger initialization capabilities.
new: Suitable for creating pointers to struct types and other basic types, and initializes the memory to the zero value.make: Used to initialize slices, maps, and channels, supports specifying capacity, and completes internal initialization.
Understanding the different usage scenarios and performance impacts of these two will help you write more efficient and maintainable Go code.
We are Leapcell, your top choice for hosting Go projects.
Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:
Multi-Language Support
- Develop with Node.js, Python, Go, or Rust.
Deploy unlimited projects for free
- pay only for usage — no requests, no charges.
Unbeatable Cost Efficiency
- Pay-as-you-go with no idle charges.
- Example: $25 supports 6.94M requests at a 60ms average response time.
Streamlined Developer Experience
- Intuitive UI for effortless setup.
- Fully automated CI/CD pipelines and GitOps integration.
- Real-time metrics and logging for actionable insights.
Effortless Scalability and High Performance
- Auto-scaling to handle high concurrency with ease.
- Zero operational overhead — just focus on building.
Explore more in the Documentation!
Follow us on X: @LeapcellHQ



