This is the 24th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Use new to assign addresses

Go has two allocation statements, 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 is a built-in function that allocates memory, but unlike functions of the same name in some other languages, it does not initialize memory, only zeroes it out. That is, new(T) allocates the zero store for a new item of type T and returns its address, a value of type *T. In Go terminology, it returns a pointer to the newly allocated zero value of type T.

Since the memory returned by new is already zero, you design your data structures with each type of zero in mind, so that when you use new you can use it without further initialization. This means that users of data structures can use new to create a piece of data and start working directly. For example, the bytes.buffer document reads: “The zero value of Buffer is an empty Buffer ready for use.” Similarly, sync.mutex has no explicit constructor or Init method. Instead, the zero value of sync.mutex is defined as an unlocked Mutex.

type SyncedBuffer struct {
    lock    sync.Mutex
    buffer  bytes.Buffer
}
Copy the code

Values of type SyncedBuffer can also be used immediately after allocation or declaration. In the next fragment, P and V work without further arrangement.

Constructors and compound literals

Sometimes zero is not good enough and the constructor needs to be initialized, such as from package OS.

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 }Copy the code

There are many templates in it. We can simplify this by using a compound 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
}
Copy the code

Note that, unlike C, it is perfectly ok to return the address of a local variable; The address associated with the variable persists after the function returns. In fact, a new instance is assigned each time the address of the compound literal is evaluated, so we can combine the last two lines.

return &File{fd, name, nil, 0}
Copy the code

The fields of a compound text are arranged in order and must all exist. However, by explicitly marking elements as field:value pairs, initializers can appear in any order, and the missing ones are retained as their respective zeros. So we can write it as

return &File{fd: fd, name: name}
Copy the code

As a limitation, if the compound literal contains no fields at all, it creates a zero value for the type. The expressions new(File) and &File{} are equivalent.

You can also create compound literals for arrays, slicing, and maps, and field labels are indexes or mapping keys, as appropriate. In these examples, the initialization works as long as the Enone, Eio, and Einval values are different.

a := [...] string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"} s := []string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"} m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"}Copy the code

\