3 回答
TA贡献1796条经验 获得超4个赞
包含自身的切片
除了递归类型(例如type Foo []Foo,参见 ANisus 的回答),它除了演示之外一无是处,如果例如切片的元素类型是interface{}:
s := []interface{}{"one", nil}
s[1] = s
在这个例子中,切片s将有 2 个接口值,第一个“包装”一个简单的 string "one",另一个接口值包装切片值本身。创建接口值时,将包装该值的副本,在切片的情况下,这意味着切片头/描述符的副本,其中包含指向底层数组的指针,因此副本将具有相同的指针值指向相同的底层数组。(有关接口表示的更多详细信息,请参阅反射定律:接口的表示。)
如果您很快就可以打印它:
fmt.Println(s)
你会得到一个致命的错误,比如:
runtime: goroutine stack exceeds 250000000-byte limit
fatal error: stack overflow
因为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])
输出(在Go Playground上试试):
one
one
one
无论我们走多深,第二个元素始终是指向与 相同数组的切片值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])
输出(在Go Playground上试试):
one
one
panic: interface conversion: interface is nil, not [2]interface {}
这是因为当数组被包装成一个时interface{},一个副本将被包装 - 并且副本不是原始数组。所以s会有第二个值,一个interface{}包裹数组,但这是一个不同的数组,它的第二个值没有设置,因此将是nil( type 的零值interface{}),所以试图“进入”这个数组会恐慌,因为它是nil(类型断言失败,因为没有使用特殊的“逗号,确定”形式)。
由于这个s数组不包含自身,一个简单的fmt.Println()将显示其全部内容:
fmt.Println(s)
输出:
[one [one <nil>]]
进一步的interface{}包装分析
如果将数组包裹在 an 中interface{}并修改原始数组的内容,则包裹在 the 中的值interface{}不受影响:
arr := [2]int{1, 2}
var f interface{} = arr
arr[0] = 11
fmt.Println("Original array: ", arr)
fmt.Println("Array in interface:", f)
输出:
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)
输出:
Original slice: [11 2]
Slice in interface: [11 2]
在Go Playground上试试这些。
TA贡献1825条经验 获得超4个赞
下面的示例创建一个包含自身的切片:
type Foo []Foo
bar := make(Foo, 1)
bar[0] = bar
这是可以做到的,因为切片值内部包含一个指向数组的指针、长度和容量。
另一方面,数组是一个值。它最多可以包含指向自身的指针。
TA贡献1780条经验 获得超1个赞
切片包含指向保存元素的内存的指针、可用元素计数的长度以及内存大小的能力。所以它喜欢:
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;我认为是indirect,因为元素是由指针引用的。当然,我们可以将切片本身放入void *data.
- 3 回答
- 0 关注
- 262 浏览
添加回答
举报
