Home  >  Article  >  Backend Development  >  nil slice, non-nil slice, empty slice in Go language

nil slice, non-nil slice, empty slice in Go language

WBOY
WBOYforward
2024-02-08 21:18:081200browse

Go 语言中 nil 切片、非 nil 切片、空切片

php editor Xiaoxin brings you an introduction to the slice types in the Go language. In Go language, slices have three states: nil slice, non-nil slice and empty slice. These three slicing states have different meanings and characteristics when used. Understanding the differences between these slice types will help us better understand and use the slice function in the Go language. Next, let’s explore the specific features and uses of these three slice types.

Question content

I am new to Go programming. I read in a Go programming book that a slice consists of three parts: a pointer to the array, the length, and the capacity.

I'm confused about the following:

  • nil slice (the slice does not point to the underlying array, len = 0, cap=0)
  • Only non-zero slices with len = 0, cap = 0
  • Empty slice.

Can anyone tell me if nil and empty slice are the same thing? If both are different then please tell me what is the difference between these two? How to test if a slice is empty? Also, what value does a pointer hold in a non-zero slice with zero length and capacity?

Solution

Observable Behavior

nil is not the same as an empty slice (capacity 0), but their observable behavior is the same (almost always). I mean:

  • You can pass them to the built-in len() and cap() functions
  • You can for range override them (will be 0 iterations)
  • You can slice them (as long as it doesn't violate the restrictions outlined in Specification: Slicing Expressions ; so the result will also be an empty slice)
  • Since their length is 0, you cannot change their contents (appending a value creates a new slice value)

Check out this simple example (one nil slice and 2 non-nil empty slices):

var s1 []int         // nil slice
s2 := []int{}        // non-nil, empty slice
s3 := make([]int, 0) // non-nil, empty slice

fmt.Println("s1", len(s1), cap(s1), s1 == nil, s1[:], s1[:] == nil)
fmt.Println("s2", len(s2), cap(s2), s2 == nil, s2[:], s2[:] == nil)
fmt.Println("s3", len(s3), cap(s3), s3 == nil, s3[:], s3[:] == nil)

for range s1 {}
for range s2 {}
for range s3 {}

Output (try it on Go Playground):

s1 0 0 true [] true
s2 0 0 false [] false
s3 0 0 false [] false

(Note that slicing a nil slice produces a nil slice, and slicing a non-nil slice produces a non-nil slice.)

With the exception that you can only tell the difference by comparing the slice value to the predeclared identifier nil, they all behave identically in other respects. But be aware that many packages do compare slices to nil and may do things differently based on that (e.g. encoding/json and fmt packages).

The only difference is converting the slice to an array pointer (added to the language in Go 1.17). Converting a non-nil slice to an array pointer produces a non-nil pointer, and converting a nil slice to an array pointer produces a nil pointer.

To determine if a slice is empty, simply compare its length to 0: len(s) == 0. Whether it's a nil slice or a non-nil slice, it doesn't matter whether it has a positive capacity; if it has no elements, it's empty.

s := make([]int, 0, 100)
fmt.Println("Empty:", len(s) == 0, ", but capacity:", cap(s))

Print (try it on Go Playground):

Empty: true , but capacity: 100

bottom

Slice values ​​are represented by structures defined in reflect.SliceHeader:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

For a nil slice, the structure will have its zero value, i.e. all its fields will have zero values, i.e.: 0.

If the capacity and length of the non-nil slice is equal to the 0, Len and Cap fields, it is most likely 0, but the Data pointer may not be. It won't , that's what differentiates it from a nil slice. It will point to a zero-sized underlying array.

Please note that the Go specification allows values ​​of different types with size 0 to have the same memory address. Specifications: System Notes: Size and Alignment Guarantees:

让我们检查一下。为此,我们调用 unsafe 包的帮助,并“获取” reflect.SliceHeader 结构“视图” “我们的切片值:

var s1 []int
s2 := []int{}
s3 := make([]int, 0)

fmt.Printf("s1 (addr: %p): %+8v\n",
    &s1, *(*reflect.SliceHeader)(unsafe.Pointer(&s1)))
fmt.Printf("s2 (addr: %p): %+8v\n",
    &s2, *(*reflect.SliceHeader)(unsafe.Pointer(&s2)))
fmt.Printf("s3 (addr: %p): %+8v\n",
    &s3, *(*reflect.SliceHeader)(unsafe.Pointer(&s3)))

输出(在 Go Playground 上尝试一下):

s1 (addr: 0x1040a130): {Data:       0 Len:       0 Cap:       0}
s2 (addr: 0x1040a140): {Data: 1535812 Len:       0 Cap:       0}
s3 (addr: 0x1040a150): {Data: 1535812 Len:       0 Cap:       0}

我们看到了什么?

  • 所有切片(切片头)都有不同的内存地址
  • nil 切片具有 0 数据指针
  • s2s3 切片确实具有相同的数据指针,共享/指向相同的 0 大小的内存值

The above is the detailed content of nil slice, non-nil slice, empty slice in Go language. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete