Golang 1.20语言特性更新
目录
语言特性⌗
允许将slice转为array⌗
相关的issue: https://github.com/golang/go/issues/46505
// slice to array
s := []int{1, 2, 3, 4, 5}
fmt.Printf("%v %p\n", s, &s)
arr := [5]int(s) // Go1.19 cannot convert s (variable of type []int) to type [5]int
fmt.Printf("%v %p\n", arr, &arr)
unsafe包增加了三个新的函数⌗
这些函数提供了完整的能力来构建和销毁slice
和string
值,而不依赖于their exact representation.
- SliceData
- String
- StringData
func main() {
var arr = [5]byte{'a', 'b', 'c', 'd', 'e'}
s := unsafe.String(&arr[0], 5)
fmt.Printf("%v %p\n", s, &s) // abcde 0xc00000e1e0
sd := unsafe.StringData(s)
s2 := unsafe.String(sd, len(s))
fmt.Printf("%v %p\n", s2, &s2) // abcde 0xc00000e200
sl := arr[:]
fmt.Printf("%v %p\n", sl, &sl) // [97 98 99 100 101] 0xc00000e210
r := unsafe.SliceData(sl)
// Go1.17已经引入了unsafe.Slice,可以直接将底层存储的指针转为切片
sl2 := unsafe.Slice(r, len(arr))
fmt.Printf("%v %p\n", sl2, &sl2) // [97 98 99 100 101] 0xc00000e220
}
在某些对性能要求比较高的项目中,会用unsafe方法来避免内存分配。比如slog的StringValue.
- https://github.com/golang/exp/blob/master/slog/value_119.go
- https://github.com/golang/exp/blob/master/slog/value_120.go
reflect
已经有reflect.SliceHeader,reflect.StringHeader在运行的slice
和string
。
但是在Go1.21中被设置为Deprecated,分别被unsafe.Slice
或者unsafe.SliceData
, 以及unsafe.String
或者unsafe.StringData
替代。(上面的slog代码就是个典型的例子)
理由如下
It cannot be used safely or portably and its representation may change in a later release. Moreover, the Data field is not sufficient to guarantee the data it references will not be garbage collected, so programs must keep a separate, correctly typed pointer to the underlying data.
关联的issue: https://github.com/golang/go/issues/53003
Comparable types⌗
放宽了comparable types的限制。原先普通的interface{}
或者any
这样的,都是被认为没有实现comparable
的,现在都是可以作为comparable
类型的。但是如果实际类型有问题,那么会在运行时有panic的风险。
package main
import "fmt"
func keys[K comparable, V any](m map[K]V) []K {
var keys []K
for k := range m {
keys = append(keys, k)
}
return keys
}
func main() {
m := make(map[any]any) // ok
fmt.Printf("%v %d\n", keys(m), len(m)) // compiler error (Go 1.19): any does not implement comparable
m[1] = 1
m["1"] = "1"
m[true] = true
fmt.Printf("%v %d\n", keys(m), len(m)) // compiler error (Go 1.19): any does not implement comparable
// func() {} 不是 comparable,但它是any,运行时会报错 hash of unhashable type func()
// k := func() {}
// m[k] = 1 // panic: runtime error: hash of unhashable type
func()
}
Go 1.20语言规范借此机会还进一步澄清了结构体和数组两种类型比较实现的规范:对于结构体类型,Go会按照结构体字段的声明顺序,逐一字段进行比较,直到遇到第一个不相等的字段为止。如果没有不相等字段,则两个结构体字段相等;对于数组类型,Go会按数组元素的顺序,逐一元素进行比较,直到遇到第一个不相等的元素为止。如果没有不相等的元素,则两个数组相等。
// struct as key
item1 := item{"item1", 1}
item2 := item{"item2", 2}
m[item1] = 1
m[item2] = 2
fmt.Printf("%v %d\n", keys(m), len(m)) // [1 1 true {item1 1} {item2 2}] 5
item3 := item{"item1", 1}
m[item3] = 3
fmt.Printf("%v %d\n", keys(m), len(m)) // [1 1 true {item1 1} {item2 2}] 5
item4 := item{price: 1, name: "item1"}
m[item4] = 4
fmt.Printf("%v %d\n", keys(m), len(m)) // [1 true {item1 1} {item2 2} 1] 5
// array as key
// 相同长度的数组,元素类型相同,元素值相同,那么它们的哈希值也是相同的
arr1 := [5]int{1, 2, 3, 4, 5}
arr2 := [5]int{1, 2, 3, 4, 5}
m[arr1] = 1
m[arr2] = 2
fmt.Printf("%v %d\n", keys(m), len(m)) //[{item2 2} [1 2 3 4 5] 1 1 true {item1 1}] 6
// 相同长度,不同顺序
arr3 := [5]int{1, 2, 3, 5, 4}
m[arr3] = 3
fmt.Printf("%v %d\n", keys(m), len(m)) // [{item1 1} {item2 2} [1 2 3 4 5] [1 2 3 5 4] 1 1 true] 7
// slice as key
// s1 := []int{1, 2, 3, 4, 5}
// s2 := []int{1, 2, 3, 4, 5}
// // panic: runtime error: hash of unhashable type []int
// m[s1] = 1
// m[s2] = 2
// fmt.Printf("%v %d\n", keys(m), len(m))