ano*_*ose 9 arrays comparison go slice
我正在尝试使用"Go Go Programming Language"来学习Golang,并且我已经达到了关于切片的部分.他们在数组和切片之间进行比较,==因为可以将两个数组与两个切片不能进行比较.该文本如下:
"== operator for arrays of strings, it may be puzzling that slice
comparisons do not also work this way. There are two reasons why deep
equivalence is problematic. First, unlike array elements, the elements
of a slice are indirect, making it possible for a slice to contain
itself. Although there are ways to deal with such cases, none is
simple, efficient, and most importantly, obvious."
Run Code Online (Sandbox Code Playgroud)
由于元素是间接的,因此切片可能包含自身的含义是什么意思?
icz*_*cza 12
除了递归类型(例如type Foo []Foo,参见ANisus的答案)之外,除了演示之外什么也没有用,如果切片的元素类型是interface{}:例如,切片可以包含自身:
s := []interface{}{"one", nil}
s[1] = s
Run Code Online (Sandbox Code Playgroud)
在此示例中,切片s将具有2个接口值,第一个"包装"一个简单的字符串"one",另一个接口值包含切片值本身.创建接口值时,将包装该值的副本,在切片的情况下,这意味着切片头/描述符的副本,其中包含指向底层数组的指针,因此副本将具有指向相同的指针值相同的底层数组.(有关接口表示的更多详细信息,请参阅"反射定律:接口的表示".)
如果您很快打印出来:
fmt.Println(s)
Run Code Online (Sandbox Code Playgroud)
你会得到一个致命的错误,例如:
runtime: goroutine stack exceeds 250000000-byte limit
fatal error: stack overflow
Run Code Online (Sandbox Code Playgroud)
因为fmt.Println()尝试递归地打印内容,并且因为第二个元素是指向正在打印的切片的相同数组的切片,所以它会进入无限循环.
另一种方法来看看它是否真的是切片本身:
s := []interface{}{"one", nil}
s[1] = s
fmt.Println(s[0])
s2 := s[1].([]interface{})
fmt.Println(s2[0])
s3 := s2[1].([]interface{})
fmt.Println(s3[0])
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
one
one
one
Run Code Online (Sandbox Code Playgroud)
无论我们走多远,第二个元素将始终是指向同一个数组的切片值s,包含在一个interface{}值中.
间接起着重要的作用,因为副本将被包装在内,interface{}但副本将包含相同的指针.
将类型更改为数组:
s := [2]interface{}{"one", nil}
s[1] = s
fmt.Println(s[0])
s2 := s[1].([2]interface{})
fmt.Println(s2[0])
s3 := s2[1].([2]interface{})
fmt.Println(s3[0])
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
one
one
panic: interface conversion: interface is nil, not [2]interface {}
Run Code Online (Sandbox Code Playgroud)
这是因为当数组被包装成一个时interface{},一个副本将被包装 - 一个副本不是原始数组.所以s将有一个第二个值,一个interface{}包装数组,但这是一个不同的数组,其第二个值没有设置,因此将是nil(类型的零值interface{}),所以试图"进入"这个数组将是恐慌,因为它是nil(类型断言失败,因为没有使用特殊的"逗号,确定"形式).
由于此s数组不包含自身,因此简单fmt.Println()将显示其完整内容:
fmt.Println(s)
Run Code Online (Sandbox Code Playgroud)
输出:
[one [one <nil>]]
Run Code Online (Sandbox Code Playgroud)
interface{}包装分析如果将数组包装在一个interface{}并修改原始数组的内容,则包含在其中的值interface{}不受影响:
arr := [2]int{1, 2}
var f interface{} = arr
arr[0] = 11
fmt.Println("Original array: ", arr)
fmt.Println("Array in interface:", f)
Run Code Online (Sandbox Code Playgroud)
输出:
Original array: [11 2]
Array in interface: [1 2]
Run Code Online (Sandbox Code Playgroud)
如果对切片执行相同操作,则包装切片(因为指向相同的基础数组)也会受到影响:
s := []int{1, 2}
f = s
s[0] = 11
fmt.Println("Original slice: ", s)
fmt.Println("Slice in interface:", f)
Run Code Online (Sandbox Code Playgroud)
输出:
Original slice: [11 2]
Slice in interface: [11 2]
Run Code Online (Sandbox Code Playgroud)
在Go Playground尝试这些.
下面的示例创建一个包含自身的切片:
type Foo []Foo
bar := make(Foo, 1)
bar[0] = bar
Run Code Online (Sandbox Code Playgroud)
之所以可以这样做,是因为slice值在内部包含指向数组的指针,长度和容量。
另一方面,数组是一个值。它充其量只能包含指向其自身的指针。