go1.20最新版本变动更新
在Go 1.20版本之前,我们以Go 1.19版本[7]为例写下下面代码:
package main
import "fmt"
func main() {
var sl = []int{1, 2, 3, 4, 5, 6, 7}
var arr = [7]int(sl) // 编译器报错:cannot convert sl (variable of type []int) to type [7]int
fmt.Println(sl)
fmt.Println(arr)
在Go 1.20版本之前如果要实现切片到数组的转换,是有trick的,看下面代码:
func main() {
var sl = []int{1, 2, 3, 4, 5, 6, 7}
var parr = (*[7]int)(sl)
var arr = *(*[7]int)(sl)
fmt.Println(sl) // [1 2 3 4 5 6 7]
fmt.Println(arr) // [1 2 3 4 5 6 7]
sl[0] = 11
fmt.Println(sl) // [11 2 3 4 5 6 7]
fmt.Println(arr) // [1 2 3 4 5 6 7]
fmt.Println(*parr) // [11 2 3 4 5 6 7]
}
该trick的理论基础是Go允许获取切片的底层数组地址。在上面的例子中parr就是指向切片sl底层数组的指针,通过sl或parr对底层数组元素的修改都能在对方身上体现出来。但是arr则是底层数组的一个副本,后续通过sl对切片的修改或通过parr对底层数组的修改都不会影响arr,反之亦然。
该trick的理论基础是Go允许获取切片的底层数组地址。在上面的例子中parr就是指向切片sl底层数组的指针,通过sl或parr对底层数组元素的修改都能在对方身上体现出来。但是arr则是底层数组的一个副本,后续通过sl对切片的修改或通过parr对底层数组的修改都不会影响arr,反之亦然。
不过这种trick语法还不是那么直观!于是上面那个“允许将切片直接转换为数组”的issue便提了出来。我们在go playground[8]上选择“go dev branch”便可以使用最新go tip的代码,我们尝试一下最新语法:
func main() {
var sl = []int{1, 2, 3, 4, 5, 6, 7}
var arr = [7]int(sl)
var parr = (*[7]int)(sl)
fmt.Println(sl) // [1 2 3 4 5 6 7]
fmt.Println(arr) // [1 2 3 4 5 6 7]
sl[0] = 11
fmt.Println(arr) // [1 2 3 4 5 6 7]
fmt.Println(parr) // &[11 2 3 4 5 6 7]
}
我们看到直接将sl转换为数组arr不再报错,但其语义与前面的“var arr = ([7]int)(sl)”语义是相同的,即返回一个切片底层数组的副本,arr不会受到后续切片元素变化的影响。
不过这里也有个约束,那就是转换后的数组长度要小于等于切片长度,否则会panic:
var sl = []int{1, 2, 3, 4, 5, 6, 7}
var arr = [8]int(sl) // panic: runtime error: cannot convert slice with length 7 to array or pointer to array with length 8
在写本文时,该issue尚未close,不过进入最终Go 1.20版本应该不是大问题