A variable is a piece of storage containing a value. In Go there are multiple
ways of creating variables. Most often, these variables are created by
declarations and are identified by name, such as the famous i
index variable.
Another way to create a variable in to use the built-in function new
.
// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type
When you use the new(T)
expression in Go, it creates an unnamed variable
of type T
, initializes it to the zero value of T
, and returns its address
as a value of type *T
. This allows you to allocate memory for a new value
of a specific type and get a pointer to that memory, without having to give
the variable a name or explicitly initialize it.
In Go, a variable that is created using the new
function is functionally
equivalent to an ordinary local variable whose address has been taken,
with the main difference being that a new variable doesn’t need to be
given a random name. As a result, new can be thought of as a syntactic
convenience rather than a fundamental feature of the language.
For instance the following two functions have identical behaviors.
// using new built-in function
func newInt() *int {
return new(int)
}
// declaring dummy variable
func newInt() *int {
var dummy int
return &dummy
}
The actual implementation of the new
function in Go is part of the Go
runtime and is written in Go itself. The implementation is quite complex
and involves a number of different components, including the garbage collector,
memory allocator, and type system. Here is a simplified version of what the
new
function could sort look like from a high level:
// new allocates memory for a single value of the specified type and returns a pointer to it.
func new(Type) *Type {
// Get the size of the type.
size := unsafe.Sizeof(Type)
// Allocate a new block of memory using the built-in make function.
ptr := make([]byte, size)
// Convert the byte slice to a pointer of the specified type.
return (*Type)(unsafe.Pointer(&ptr[0]))
}
In this implementation, the new function takes a single parameter of type Type
,
which is the type of the value that will be allocated. The unsafe package is
used to work with low-level memory operations that are not guaranteed to be safe
or portable. The unsafe.Sizeof
function is used to get the size of the type in
bytes, which is needed to allocate the correct amount of memory. The make function
is used to allocate a new block of memory as a byte slice of the appropriate size.
Finally, the unsafe.Pointer
function is used to convert the byte slice to a pointer
of the specified type. This pointer is returned to the caller as a *Type
.
You would typically use the new function when you need to create a new value of a specific type, and you want to initialize it to its zero value. This is useful when you need to allocate memory for a new value on the heap, rather than on the stack. This can be useful in situations where you need to create a temporary value or pass a pointer to a function without needing to access the value directly.
Note that the new function is rarely used in idiomatic Go code, because most values are allocated on the stack rather than the heap. Furthermore, Go programmers often prefer to use type literals or type declarations to create and initialize variables, as this can be more explicit and easier to read. In some cases, using a type literal or type declaration can also be more efficient than using new, as it allows the Go compiler to optimize the code more effectively.
References
- Donovan, A.A.A. and Kernighan, B.W. (2020) The go programming language. New York, N.Y: Addison-Wesley.