func new
func new(Type) *Type
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 make
func make(Type, size IntegerType) Type
The make built-in function allocates and initializes an object of type slice, map, or chan (only). Like new, the first argument is a type, not a value. Unlike new, make's return type is the same as the type of its argument, not a pointer to it. The specification of the result depends on the type:
Slice: The size specifies the length. The capacity of the slice is equal to its length. A second integer argument may be provided to specify a different capacity; it must be no smaller than the length, so make([]int, 0, 10) allocates a slice of length 0 and capacity 10. Map: An empty map is allocated with enough space to hold the specified number of elements. The size may be omitted, in which case a small starting size is allocated. Channel: The channel's buffer is initialized with the specified buffer capacity. If zero, or the size is omitted, the channel is unbuffered.
1、Allocation with new
Go has two allocation primitives, the built-in functions
new
and make
. They do different things and apply to different types, which can be confusing, but the rules are simple. Let's talk about new
first. It's a built-in function that allocates memory, but unlike its namesakes in some other languages it does not initialize the memory, it only zeros it. That is, new(T)
allocates zeroed storage for a new item of type T
and returns its address, a value of type *T
. In Go terminology, it returns a pointer to a newly allocated zero value of type T
.
Since the memory returned by
new
is zeroed, it's helpful to arrange when designing your data structures that the zero value of each type can be used without further initialization. This means a user of the data structure can create one with new
and get right to work. For example, the documentation for bytes.Buffer
states that "the zero value for Buffer
is an empty buffer ready to use." Similarly, sync.Mutex
does not have an explicit constructor or Init
method. Instead, the zero value for a sync.Mutex
is defined to be an unlocked mutex.
The zero-value-is-useful property works transitively(可传递的). Consider this type declaration.
type SyncedBuffer struct { lock sync.Mutex buffer bytes.Buffer }
Values of type SyncedBuffer
are also ready to use immediately upon allocation or just declaration. In the next snippet, both p
and v
will work correctly without further arrangement.
p := new(SyncedBuffer) // type *SyncedBuffer var v SyncedBuffer // type SyncedBuffer
2、Constructors and composite literals(复合字面量)
Sometimes the zero value isn't good enough and an initializing constructor is necessary, as in this example derived from packageos
.
func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := new(File) f.fd = fd f.name = name f.dirinfo = nil f.nepipe = 0 return f }There's a lot of boiler plate in there. We can simplify it using a composite literal , which is an expression that creates a new instance each time it is evaluated.
func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := File{fd, name, nil, 0} return &f }Note that, unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. In fact, taking the address of a composite literal allocates a fresh instance each time it is evaluated, so we can combine these last two lines.
return &File{fd, name, nil, 0}The fields of a composite literal are laid out in order and must all be present. However, by labeling the elements explicitly as field
:
value
pairs, the initializers can appear in any order, with the missing ones left as their respective zero values. Thus we could say
return &File{fd: fd, name: name}As a limiting case, if a composite literal contains no fields at all, it creates a zero value for the type. The expressions
new(File)
and
&File{}
are equivalent.
Composite literals can also be created for arrays, slices, and maps, with the field labels being indices or map keys as appropriate.
a := [...]string{1: "no error", 2: "Eio", 3: "invalid argument"}
s := []string{1: "no error", 2: "Eio", 3: "invalid argument"}
m := map[int]string{1: "no error", 2: "Eio", 3: "invalid argument"}
3、Allocation with make
Back to allocation. The built-in function
make(T,
args)
serves a purpose different from new(T)
. It creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T
(not *T
). The reason for the distinction is that these three types represent, under the covers, references to data structures that must be initialized before use. A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the length, and the capacity, and until those items are initialized, the slice is nil
. For slices, maps, and channels, make
initializes the internal data structure and prepares the value for use. For instance,
make([]int, 10, 100)allocates an array of 100 ints and then creates a slice structure with length 10 and a capacity of 100 pointing at the first 10 elements of the array. (When making a slice, the capacity can be omitted; see the section on slices for more information.) In contrast,
new([]int)
returns a pointer to a newly allocated, zeroed slice structure, that is, a pointer to a
nil
slice value.
These examples illustrate the difference between
new
and make
.
var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints // Unnecessarily complex: var p *[]int = new([]int) *p = make([]int, 100, 100) // Idiomatic(符合语言习惯的): v := make([]int, 100)Remember that
make
applies only to maps, slices and channels and does not return a pointer. To obtain an explicit pointer allocate with
new
or take the address of a variable explicitly.