新增内置函数

min和max

相关issue:https://github.com/golang/go/issues/59488

在builtin.go中的函数定义如下(实际的实现在runtime中,通过编译器的配合实现调用):

// The max built-in function returns the largest value of a fixed number of
// arguments of [cmp.Ordered] types. There must be at least one argument.
// If T is a floating-point type and any of the arguments are NaNs,
// max will return NaN.
func max[T cmp.Ordered](x T, y ...T) T

// The min built-in function returns the smallest value of a fixed number of
// arguments of [cmp.Ordered] types. There must be at least one argument.
// If T is a floating-point type and any of the arguments are NaNs,
// min will return NaN.
func min[T cmp.Ordered](x T, y ...T) T

哪些类型符合cmp.Ordered呢?可以通过go doc cmp.Ordered查看

type Ordered interface {
  ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
    ~float32 | ~float64 |
    ~string
}

demo

clear

相关issue: https://github.com/golang/go/issues/56351

builtin.go中的函数定义如下:

// The clear built-in function clears maps and slices.
// For maps, clear deletes all entries, resulting in an empty map.
// For slices, clear sets all elements up to the length of the slice
// to the zero value of the respective element type. If the argument
// type is a type parameter, the type parameter's type set must
// contain only map or slice types, and clear performs the operation
// implied by the type argument.
func clear[T ~[]Type | ~map[Type]Type1](t T)

只有map和slice类型才能调用clear函数,如果是类型参数,那么类型参数的类型集合必须只包含map或者slice类型。

但是map和slice处理方式不同

  • clear map:删除所有的键值对,map变成空map,但并未释放掉这些键值所占用的内存。(关于map内存释放的问题,另有一个open的issue)
  • clear slice:将保持slice的长度和容量,但是所有值都被置为零值。

demo

明确了包初始化顺序

请见Go1.21包初始化顺序变更

类型推导(type inference)增强

具体可见Go1.21类型推断增强

loop variables per-iteration替代per-loop

相关issue:

相关wiki:

Go团队可能会在Go1.22中将loop variables per-iteration替代per-loop,这是一个breaking change,如果你的代码中有类似以下的代码,那么你的代码可能会受到影响。

  var m = map[string]int{"a": 1, "b": 2}
  for k, v := range m {
    go func() {
      time.Sleep(1 * time.Second)
      fmt.Printf("k: %v, v: %v\n", k, v)
    }()
  }

以上这段代码正常执行会输出

k: b, v: 2 k: b, v: 2

loop var的变量k, v在每个loop中都指向同一个,所以在goroutine中打印的k, v都是最后一次循环的值。

如果想要每次goroutine中每次输出的值为对应loop的k, v值,那么有以下两种规避的方法。

方法1: 给goroutine闭包传参

  for k, v := range m {
    go func(k string, v int) {
      time.Sleep(1 * time.Second)
      fmt.Printf("k: %v, v: %v\n", k, v)
    }(k, v) 
  }

方法2: 在每个loop内使用本地变量

  for k, v := range m {
    var k1, v1 = k, v
    go func() {
      time.Sleep(1 * time.Second)
      fmt.Printf("k: %v, v: %v\n", k1, v1)
    }()
  }

Go1.22可能会改变当前默认行为(实际情况要等到1.22发布),让上面例子中每个loop的k, v都是单独分配的。而在Go1.21中,可以使用GOEXPERIMENT=loopvar

panic(nil)

相关issue:

func main() {
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("panic:", err)
      return
    }
    fmt.Println("ok")
  }()
  panic(nil)
}

在Go1.20中,上面的代码会打印出

ok 而在Go1.21中,上面的代码会打印出 panic: panic called with nil argument

在Go1.21中,编译器会自动将panic(nil)替换panic(new(runtime.PanicNilError))

这是个breaking change,如果你的代码中有panic(nil),并想保留原有的行为,那么可以使用GODEBUG=panicnil=1

demo code

参考