七叶笔记 » golang编程 » 重磅:Rob 反对 1.18 修改标准库支持泛型,附泛型 filter 实现

重磅:Rob 反对 1.18 修改标准库支持泛型,附泛型 filter 实现

还不太清楚Go1.17 泛型 基本用法的同学可以看下站长之前写的 Go泛型系列:提前掌握 Go 泛型的基本使用[1] 。正好看此文也可以简单复习一下。今天土拨鼠给大家介绍下 泛型过滤(Filter)功能 的小短文(参考 Implementing a Generic Filter Function in Go[2] )。例子也很简单,希望大家能从中有所收获。

正好昨天robpike昨天提出了 反对在Go1.18标准库中加入泛型支持 的issue。大家也可以发表一下自己的看法。

功能实现:过滤一下切片中符合条件的元素,并返回

正常情况下的话土拨鼠会简单写一下,一般都是作为一次性使用。

简单版

 //  filter  过滤掉不符合条件的元素
func filter(items []interface{}) []interface{} {
    filteredItems := []interface{}{}
    for index, value := range items {
        // 过滤掉奇数
        if num.(int)%2 == 0 {
            filteredItems = append(filteredItems, value)
        }
    }
    return filteredItems
}
  

显然这样写太简单了,一点都不够优雅。条件函数的话这里可以考虑用闭包作为函数参数来稍微优化一下。这样就可以编写各种花样的过滤逻辑咯。

优化版

 // filter 过滤掉不符合条件的元素
func filter(items []interface{}, fn func(index int, item interface{})  bool ) []interface{} {
    filteredItems := []interface{}{}
    for index, value := range items {
        if fn(index, value) {
            filteredItems = append(filteredItems, value)
        }
    }
    return filteredItems
}

func main() {
    var nums []interface{}
    nums = append(nums, 1, 2, 3, 4, 5)
    evenNums := filter(nums, func(index int, num interface{}) bool { return num.(int)%2 == 0 })
    
    fmt.Printf("%d", evenNums[0].(int) + 2)
}
  

约束版

为了使得类型使用更加清晰明了一些,咱们这里使用 any 类型(可不是随便起的哦,可以参考 类型参数提案中any的约束[3] )来替代 interface{} 类型。修改如下:

 type any = interface{}

// filter 过滤掉不符合条件的元素
func filter(items []any, fn func(index int, item any) bool) []any {
    filteredItems := []any{}
    for index, value := range items {
        if fn(index, value) {
            filteredItems = append(filteredItems, value)
        }
    }
    return filteredItems
}
  

这里给大家讲一下什么是约束?约束主要是 确保泛型类型的元素始终满足特定的接口类型 。根据 Go 的哲学,必须在类型参数声明旁边添加约束。这个demo使用的是 any 类型等同于 interface{} ,可以接受任意类型的。所以这里的约束作用就由闭包来做了。

 func filter[T SomeInterface](items []T )
  

大家也可以在 go2goPlayground[4] 进行测试,最终demo如下:

 package main

import (
   "fmt"
)

// filter 过滤掉不符合条件的元素
func filter[T any](items []T, fn func(item T "T any") bool) []T {
    filteredItems := []T{}
    for _, value := range items {
        if fn(value) {
            filteredItems = append(filteredItems, value)
        }
    }
    return filteredItems
}

func main() {
    nums := []int{1, 2, 3, 4, 5}
    evenNums := filter(nums, func(num int) bool { return num%2 == 0 })
    fmt.Println(evenNums)
}
  

关于robpike反对在Go1.18标准库中加入泛型的讨论

土拨鼠也很赞同robpike的说法,我们还需要更多的学习、使用、思考、沉淀。

下面是土拨鼠对robpike关于 go: don’t change the libraries in 1.18[5] 的翻译,关于Go1.18标准库中要不要加入泛型的支持的问题欢迎大家留言讨论。


Go语言的1.18版本很可能包括迄今为止该语言自创建以来的最大变化:参数化多态性,俗称泛型。关于核心库将如何适应,以及如何适应,已经有很多讨论。例如,可以看 #45955-slices: new package to provide generic slice functions[6] 也可以看一下煎架写的Go 提案:增加泛型版 slices 和 maps 新包和 #48594-proposal: bytes: add Grow, Clip; maybe add bytes/strings Insert, Delete[7] 的相关讨论,还有其他的已经有了,而且肯定很快会有更多。

如何在标准库中使用这些想法,需要深思熟虑和计划。现在把它们放进标准库里,也会给版本的发布带来很大的负担。

我建议不在Go1.18中更新标准库。

理由很简单,也很有说服力。 一下子要做的事情太多,而且我们可能会弄错 。语言的改变已经以某种形式进行了十多年,但库的改变是非常新的, 我们没有在Go中使用新类型的经验 ,无法为其设计提供有力的依据。是的,我们可以对它们进行详细的推理,而且已经做了很多。其他语言的经验是有帮助的,但是 Go教会了我们一件事,那就是它有自己做事的方法。

对于泛型,我们还不知道那些新的方法是什么。另外,兼容性的承诺使我们在任何细节上出错的代价都很高。我们应该等待、观察和学习。

相反,我 建议我们仍然可以设计、构建、测试和使用用于slice、 map 、channel等的新库,但首先要把它们放在 golang /x/exp 仓库中 。这样一来,这些新的库-在现阶段是真正实验性的-可以在生产中进行测试,但可以在一两个周期内进行改变、调整和发展,让整个社区尝试一下,如果他们有兴趣并且愿意接受一点不稳定的话,而不要求每个组件的每个细节都从第一天就准备好。 一旦它们经过一段时间的试验,并通过经验进行更新,我们就会把它们移到主 repo 中,就像我们对其他外部生成的 软件包 所做的那样,但我们相信它们在实践中运行良好,值得我们做出的兼容性承诺

我意识到每个人都想体验新语言功能带来的乐趣,并期待着修复核心库中的一些问题,一旦它到来,就不会那么笨拙,但我坚信现在最好慢慢来。使用、学习、研究,并谨慎地做出改动。

参考资料

[1]

Go泛型系列:提前掌握Go泛型的基本使用:

[2]

Implementing a Generic Filter Function in Go:

[3]

类型参数提案中any的约束: #the-constraint

[4]

go2goPlayground:

[5]

go: don’t change the libraries in 1.18:

[6]

#45955-slices: new package to provide generic slice functions:

[7]

#48594-proposal: bytes: add Grow, Clip; maybe add bytes/strings Insert, Delete:

相关文章