语言特性

允许将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包增加了三个新的函数

这些函数提供了完整的能力来构建和销毁slicestring值,而不依赖于their exact representation.

  1. SliceData
  2. String
  3. 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.

reflect已经有reflect.SliceHeaderreflect.StringHeader在运行的slicestring。 但是在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))

测试代码

https://github.com/meirongdev/go_1.20_exploration