Go (or Golang) is a modern, statically typed, compiled programming language designed for building scalable, concurrent, and efficient software. It comes with various built-in functions and features that help developers write concise and performant code. Among these functions are new()
and make()
, which might seem similar at first but serve different purposes and are crucial for memory allocation and data initialization in Go.
In this blog post, we will explore the differences between new()
and make()
functions and understand when and how to use them effectively.
new()
and make()
Functions
Both new()
and make()
are built-in functions in Go, used to allocate memory. However, they are used for different data types and scenarios:
new()
function:new()
is used to allocate memory for value types (e.g., integers, floats, structs) and returns a pointer to the newly allocated zeroed value.It takes a single argument, which is a type, and returns a pointer to that type.
make()
function:make()
is used to create and initialize slices, maps, and channels, which are reference types in Go.It takes two or three arguments, depending on the type, and returns an initialized (not zeroed) value ready for use.
Understanding new()
Function
The syntax of the new()
function is straightforward as shown below.
func new(Type) *Type
Here, Type
represents the type of the value we want to allocate memory for. Let's see an example of how to use new()
In this example, we create a new instance of the Person
struct using new()
and then assign values to its fields using the pointer.
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
// Using new() to allocate memory for a Person struct
p := new(Person)
fmt.Printf("%T\n", p)
// Accessing struct fields using the pointer
p.Name = "Alice"
p.Age = 30
// Displaying the values
fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
}
This program will produce an output as shown below
> go run main.go
*main.Person
Name: Alice
Age: 30
Understanding make()
Function
The syntax of the make()
function varies depending on the type it is used with
For Slices
func make([]Type, len, cap) []Type
Type
: The type of elements the slice will hold.len
: The initial length of the slice.cap
: The capacity of the slice, which is optional and used to specify the underlying array's capacity. If not provided, it defaults to the same value as the length.
Example of creating a slice using make()
:
package main
import "fmt"
func main() {
// Using make() to create a slice of integers
numbers := make([]int, 5, 10)
// Displaying the slice's length, capacity, and values
fmt.Println("Length:", len(numbers))
fmt.Println("Capacity:", cap(numbers))
fmt.Println("Values:", numbers)
// Using make() to create a slice of integers
numbersWithoutOptional := make([]int, 5)
// Displaying the slice's length, capacity, and values
fmt.Println("Length:", len(numbersWithoutOptional))
fmt.Println("Capacity:", cap(numbersWithoutOptional))
fmt.Println("Values:", numbersWithoutOptional)
}
This program will produce an output as below
> go run main.go
Length: 5
Capacity: 10
Values: [0 0 0 0 0]
Length: 5
Capacity: 5
Values: [0 0 0 0 0]
For Maps
func make(map[KeyType]ValueType, initialCapacity int) map[KeyType]ValueType
KeyType
: The type of keys in the map.ValueType
: The type of values associated with the keys.initialCapacity
: The initial capacity of the map. This is optional but can be used to optimize performance when the number of elements is known in advance.
Example of creating a map using make()
package main
import "fmt"
func main() {
// Using make() to create a map of string keys and int values
scores := make(map[string]int)
// Adding values to the map
scores["Alice"] = 95
scores["Bob"] = 87
// Displaying the map
fmt.Println("Scores:", scores)
}
> go run main.go
Scores: map[Alice:95 Bob:87]
For Channels
func make(chan Type, capacity int) chan Type
Type
: The type of values that can be sent and received through the channel.capacity
: The buffer size of the channel. If set to 0, the channel is unbuffered.
Example of creating a channel using make()
package main
import (
"fmt"
"time"
)
func main() {
// Using make() to create an unbuffered channel of integers
ch := make(chan int)
// Sending data into the channel using a goroutine
go func() {
for i := 1; i <= 5; i++ {
ch <- i
time.Sleep(time.Second) // Simulating some work before sending the next value
}
close(ch)
}()
// Receiving data from the channel
for num := range ch {
fmt.Println("Received:", num)
}
}
> go run main.go
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Conclusion
In this blog post, we have demystified the new()
and make()
functions in Go and explained their differences and use cases. To summarize:
Use
new()
to allocate memory for value types and obtain a pointer to the zeroed value.Use
make()
to create and initialize slices, maps, and channels (reference types) with their respective types and initial capacities.
Understanding the distinctions between new()
and make()
is crucial for efficient memory allocation and data initialization in Go. Properly applying these functions will lead to cleaner and more optimized code in your Golang projects. Happy coding!