SoFunction
Updated on 2025-03-02

The use of ni, zero value and empty structure in GO language

Beginners of go language, especially Java developers, are learning go languages. They are easy to confuse some concepts similar to Java but have differences, such as Go.Zero value, nil and empty structure. This article will discuss in detail the meaning of these special concepts in go and their applications in actual scenarios:

Zero value

The Zero Value can be regarded as a default initial value assigned to the variable when you declare a variable but without explicit initialization. The official definition of zero value is as follows:

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, “” for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

Based on this we can summarize:

  • Value TypeThe Boolean type is false, the value type is 0, and the string is "", and the array and struct will recursively initialize their elements or fields, that is, their initial value depends on the element or field. The so-called value type here is actually equivalent to that in JavaprimaryType, it is just to note that string is an object type in java, while string is a value type in go.

  • Reference TypeAll are nil, including pointer pointer, function function, interface interface, slice slice, pipeline channel, map map.

tip: In fact, there is no real reference type in Go. It can be roughly understood that variables of value type directly store values. Variables of reference type store an address, which is used to store the final value.

Value Type

Because there is a zero value, when using variables, we can use them directly without initializing them in most cases. This can keep the code simplicity and avoid the common NullPointerException in Java development. The following are some examples:

package main

import "sync"

type Value struct {
    mu    //No initialization is required, the declaration can be used    val int
}

func (v *Value)Incr(){
    defer ()
    ()
    ++
}

func main() {
    var i Value
    ()
}

Essentially a structure:

// A Mutex is a mutual exclusion lock.
// The zero value for a Mutex is an unlocked mutex.
//
// A Mutex must not be copied after first use.
type Mutex struct {
	state int32
	sema  uint32
}

So if it is a reference type and the zero value is nil, can't it be used directly? This actually depends on the situation. We will look at it one by one according to the type:

Slices

The zero value of the slice is a nil slice. Except for the inability to query according to the sequence index, all other operations can be done:

func testNilSlice() {
	var nilSlice []string
    (nilSlice == nil) // true
    (nilSlice[0]) //index out of range
	emptySlice = append(nilSlice, "dd") // The append operation will automatically expand the capacity	(nilSlice[0]) //Output dd}

The difference between nil slice and not nil slice:

type Person {
  Friends []string
}

var f1 []string // nil slicejson1, _ := (Person{Friends: f1})
("%s\n", json1) //output:{"Friends": null}

f2 := make([]string, 0) //non-nil empty slice, equivalent to f2 := []string{}json2, _ := (Person{Friends: f2})
("%s\n", json2) //output: {"Friends": []}

It is recommended to declare empty slices without special needs when using var nilSlice []string:/golang/go/wiki/CodeReviewComments#declaring-empty-slices

Map

For nil map, we can simply regard it as a read-only map and cannot write, otherwise it will be panic:

func testNilMap() {
	var m map[string]string
	(m["key"]) //Output ""    m["key"]="value" //panic: assignment to entry in nil map
}

So what is the use of nil map? You can take a look at the following examples:

func NewGet(url string, headers map[string]string) (*, error) {
	req, err := (, url, nil)
	if err != nil {
		return nil, err
	}
	for k, v := range headers {
		(k, v)
	}
	return req, nil
}

//If there is no header when calling this method, you can pass an empty map, for example:NewGet("", map[string]string{})
//You can also directly pass into nilNewGet("", nil)

Channel

nil channel will block all reads and writes to the channel. Therefore, you can set a channel to nil and force blocking. For the select branch, it is to force disable this branch.

func addIntegers(c chan int) {
    sum := 0
    t := ()
    for {
        select {
            case input := <-c:
                sum = sum + input
            case <-:
                c = nil
                (sum)  // Output 10        }
    }
}

func main() {
	c := make(chan int, 1)
	go addIntegers(c)
	for i := 0; i <= 10; i++ {
		c <- i
		((200) * )
	}
}

Pointers

If the pointer is nil, dereference to the pointer will cause a null pointer error that we are very familiar with in Java.

type Person struct {
	Name string
	Sex  string
	Age  int
}

var p *Person
()  // panic: runtime error: invalid memory address or nil pointer dereference

Magic nil

nil is a pre-declared identifier in Golang, which is mainly used to represent zero values ​​of reference types (pointers, interfaces, functions, maps, slices, and channels), indicating their uninitialized values.

// [src/builtin/](/src/builtin/#L98)
//
// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

nil is not a keyword or reserved word in Go language, so you can use nil as a variable name (for death):

var nil = ("my god")

nil does not have a default type, so you cannot assign a variable of an undeclared type, nor can you compare it with yourself:

a := nil
// cannot declare variable as untyped nil: a
(nil == nil)
// invalid operation: nil == nil (operator == not defined on nil)
("%T", nil)
// use of untyped nil

When comparing nil, be sure to note that nil actually has types., different types of nils are not equal, such as the following example:

var p *int
var i interface{}

(p)      // <nil>
(i)      // <nil>

(p == i) // false

Let’s look at another mistake that is easy to make in actual encoding:

type BusinessError struct {
	error
	errorCode int64
}

func doBusiness() *BusinessError {
	return nil
}

func wrapDoBusiness() error {
	err := doBusiness()
	return err
}

func testError() {
	err := wrapDoBusiness() //The essentially the nil obtained here is <T:*BusinessError,V:nil> nil>	(err == nil)
}

suggestion:If there is logic to determine whether the interface is nil value anywhere, do not write any code about assigning the interface to a specific implementation type (possibly nil). If it is nil value, it will be assigned to the interface directly, and do not convert the specific type

type BusinessError struct {
	error
	errorCode int64
}

func doBusiness() *BusinessError {
	return nil
}

func wrapDoBusiness() error {
	err := doBusiness() 
	if err == nil {
		return nil  //If the return value is nil, return nil directly, do not perform type conversion	} else {
		return err
	}
}

func testError() {
	err := wrapDoBusiness()
	(err == nil)
}

Empty structure

golang Normal struct is an ordinary memory block, which must occupy a small piece of memory, and the size of the structure must pass through the boundary and length, but the "empty structure" does not occupy memory, and the size is 0;

var q struct{}
((q)) // 0

The core reason why empty structure struct{ } exists is to save memory. When you need a structure, but it has nothing to do with the content inside, you can consider an empty structure. Here are a few classic uses:

map & struct{}

The general combination posture of map and struct {} is as follows:

// Create mapm := make(map[int]struct{})
// Assignmentm[1] = struct{}{}
// Determine whether the key key exists or not_, ok := m[1]

Generally, the combination of map and struct {} is: only care about keys, not value. For example, if you query whether the key exists, you can use this data structure to determine whether the key exists by the value of ok. The query complexity of map is O(1), and the query is very fast. This method can play a role similar to Set in Java in some scenarios

chan & struct{}

The combination of channel and struct{} is the most classic scenario. struct{} is usually transmitted as a signal and does not pay attention to the content. The essential data structure of chan is a management structure plus a ringbuffer. If struct{} is used as an element, ringbuffer is allocated by 0.

There is basically only one usage of chan and struct{}, that is,Signal delivery, the empty structure itself cannot carry the value, so this is the only way to use it. Generally speaking, it is used with the channel of no buffer.

waitc := make(chan struct{})

// ...
goroutine 1:
    // Send signal: Deliver element    waitc &lt;- struct{}
    // Send signal: Close    close(waitc)

goroutine 2:
    select {
    // Receive the signal and make the corresponding action    case &lt;-waitc:
    }

This is the end of this article about ni, zero value and empty structure in GO language. For more related GO ni zero value and empty structure content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!