• go 1.18新特性(泛性 模糊测试 WorkSpace)


    泛型

    interface的功能扩展

    在1.18之前,官方对interface的定义是方法的集合,可以存储实现该方法的任意类型。1.18对interface的定义改为类型集合。接口类型的变量可以存储这个接口类型集合的任意一种类型的实例值。

    下面是通过接口定义的一个类型集:

    type MyNum interface {
    	int32 | int64 | float32 | float64 
    }
    
    • 1
    • 2
    • 3

    我们在定义类型集的时候,可以在具体类型前加上符号**~** ,表示该类型以及它的别名类型都是该类型集的。

    泛型使用

    如下是泛型的使用:

    func Sum[T MyNum](nums []T) (rnt T) {
    
    	for _, num := range nums {
    		rnt += num
    	}
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 方法名后面**[]** 中的内容称为泛型参数列表。
    • T 是泛型参数名,自定义的。
    • MyNum 是约束类型,可以是接口定义的一个类型集合。

    还有一些变式的用法,如下:

    func Sum[T int64 | float32](nums []T) (rnt T) 
    
    func Sum[T interface{}](nums []T) (rnt T) // interface 表示任意类型
    
    • 1
    • 2
    • 3

    除了方法可以使用泛性,struct也可以使用泛型:

    type Queue[T MyNum] struct {
    	elements []T
    }
    
    • 1
    • 2
    • 3

    模糊测试

    模糊测试可以先程序或函数输入大量的无效数据,以检测个别输入带来的意外行为,以此来定位错误。 这种测试框架可以帮助发现可能导致崩溃或其他安全问题的未定义行为。 虽然不可能用模糊测试找到所有的错误,但它可以成为发现和修复许多常见类型问题的有效方法。 它经常被用来测试那些处理来自不受信任来源的输入或可能有意外输入的程序。

    模糊测试的使用

    要测试的方法:

    func Equal(a []byte, b []byte) bool {
    	if len(a) != len(b) {
    		return false
    	}
        // 模拟意外情况
    	if len(a) > 99 {
    		panic("error")
    	}
    	for i := range a {
    		if a[i] != b[i] {
    			return false
    		}
    	}
    	return true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在以_test结尾的测试文件中:

    func FuzzEqual(f *testing.F) {
    	f.Fuzz(func(t *testing.T, a []byte, b []byte) {
    		Equal(a, b)
    	})
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用命令go test . -fuzz 运行测试

    在未发现错误时,控制台输出:

    在这里插入图片描述

    就注释而言,elapsed 是测试运行的时间,new interesting 是添加到语料库中提供独特结果的输入数量,execs 是运行的单个测试的数量。

    遇到错误时:

    在这里插入图片描述

    Failing input written to 后面跟的、链接是发生错误的样例文件。

    模糊测试的缺点

    • 模糊测试是通过随机输入的方式错误,并不能覆盖全部错误。
    • 模糊测试只能告知在某个输入的情况下会出错,具体错误还需要测试人员调试寻找。

    Go WorkSpace

    在开发个过程中,如果多个module同时开发,并且module之间存在相互依赖的关系,可能会出现频繁升级依赖包的问题。

    有如下场景:

    在这里插入图片描述

    一个项目同时在本地开发两个模块,A和B,并且A依赖B,当B升级后,A如何升级自己依赖的包?

    1. 将module B 添加tag push到代码仓库,moudle 使用个go get 命令重新拉取。显然这种方法十分麻烦,A每次升级依赖包都需要进行一遍这样的流程。

    2. 可以使用go mod 的replace 命令,直接使用本地的最新代码,将A的go.mod进行如下更改:

      module example.com/A
      
      go 1.18
      
      require example.com/B v1.0.0
      
      replace example.com/B => ../B
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      使用replace 命令后,尽管module B更新了 ,module A也可以使用最新的代码。但是同样存在一个问题,其他开发者下载该代码后,在编译时会报错,因为他的本地可能没有module B的代码。所以在使用这种方法时,在提交代码时要将replace 命令删除。

    为了解决go mod 无法很好处理本地依赖包频繁升级的问题,go 1.18引入了go workspace机制。

    在A和B模块的父目录执行命令go word init ./A ./B ,就会生成一个go .work 文件,文件目录结构如下:

    ├── A
    │   ├── A.go
    │   └── go.mod
    ├── B
    │   ├── B.go
    │   └── go.mod
    └── go.work
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    go.work 文件的内容如下:

    go 1.18
    
    use (
    	./A
    	./B
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这样我们就可以在不修改go mod 情况下,访问本地其他模块的最新内容。

    建立项目 workspace 工作区之后,对项目的依赖包的修改不再需要进行实时升级,只需要在本地进行修改,等到项目代码提交时,一并升级即可,使用时有点类似于go mod 的replace,但是提交时并不需要修改go mod文件。

  • 相关阅读:
    Kafka 和 Spring整合Kafka
    网站备份很重要:WordPress七牛云镜像存储插件,一键镜像静态资源到七牛云
    大厂镜像库
    鸿鹄工程项目管理系统em Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统
    【c++入门(2)】搜索2
    【UE5】Pixel Streaming 配置https协议
    VMware ifconfig ip报错解决记录
    基于nodejs+vue语言的酒店管理系统
    【pygame游戏】用Python实现一个蔡徐坤大战篮球的小游戏,可还行?【附源码】
    团子杂记:SAP收入确认工具RAR(Revenue Recognition&Reporting)在新收入准则下的应用
  • 原文地址:https://blog.csdn.net/m0_62969222/article/details/132911521