包含自身的切片
除了递归类型(例如type Foo []Foo
,请参阅 ANisus 的答案),除了演示之外没有任何用处,如果切片的元素类型是,则切片可能包含自身interface{}
:
s := []interface{}{"one", nil}
s[1] = s
在此示例中,切片s
将有 2 个接口值,第一个“包装”一个简单的字符串"one"
,以及包装切片值本身的另一个接口值。创建接口值时,将包装该值的副本,在切片的情况下,这意味着切片标头/描述符的副本,其中包含指向底层数组的指针,因此该副本将具有指向的相同指针值相同的底层数组。 (有关接口表示的更多详细信息,请参见反射定律:接口的表示 http://blog.golang.org/laws-of-reflection#TOC_3..)
如果您很快就开始打印它:
fmt.Println(s)
你会得到一个致命错误,比如:
runtime: goroutine stack exceeds 250000000-byte limit
fatal error: stack overflow
Because 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])
输出(尝试一下去游乐场 http://play.golang.org/p/Uj191SWibK):
one
one
one
无论我们走多深,第二个元素始终是指向同一个数组的切片值s
,包裹在interface{}
value.
间接寻址起着重要作用,因为副本将被包装在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])
输出(尝试一下去游乐场 http://play.golang.org/p/yz71cZ_bSF):
one
one
panic: interface conversion: interface is nil, not [2]interface {}
这是因为当数组被包装成interface{}
,副本将被包装 - 并且副本不是原始数组。所以s
将有第二个值,interface{}
包装一个数组,但这是一个不同的数组,其第二个值未设置,因此将是nil
(类型的零值interface{}
),因此尝试“进入”这个数组将会出现恐慌,因为它是nil
(类型断言 https://golang.org/ref/spec#Type_assertions失败是因为没有使用特殊的“逗号,ok”形式)。
从此s
数组不包含自身,一个简单的fmt.Println()
将揭示其完整内容:
fmt.Println(s)
Output:
[one [one <nil>]]
Further 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)
Output:
Original array: [11 2]
Array in interface: [1 2]
如果对切片执行相同的操作,则包装的切片(因为指向相同的底层数组)也会受到影响:
s := []int{1, 2}
f = s
s[0] = 11
fmt.Println("Original slice: ", s)
fmt.Println("Slice in interface:", f)
Output:
Original slice: [11 2]
Slice in interface: [11 2]
尝试这些去游乐场 http://play.golang.org/p/KrQQDkVXNf.