Go 1.18.1 Beta 尝鲜

昨天,go 终于发布了 1.18 的 beta 版本, 带来了大家期待已久的泛型,抓紧时间康康能不能赶上热乎的。

下载地址

根据社区昨天发的 Go 1.18 Beta 1 is available, with generics 这次版本更新主要带来的新功能有:

  • 泛型
  • 模糊测试( fuzzing-based tests)
  • workspace mode
  • arm64 和 PPC64 也增加了基于寄存器的调用规约
  • 增加了一个 go version -m 可以记录构建细节
  • 其他,参见 draft release notes for Go 1.18

泛型

在没有泛型之前,假设我们需要求两个数的和,根据运算数的类型,可能需要写很多个函数,如:

package mainfunc SumInt64(a, b int64) int64 {return a + b}func SumFloat64(a, b int64) float64 {return a + b}

有了泛型之后就可以这样写了:

package mainfunc Sum2[V int | int64 | float64 | int32 | float32](a, b V) V {return a + b
}

上面的代码在 [] 中声明了一个泛型 V 它支持 int, int64, int32, float32, float64 五种类型,函数有两个 V 类型的参数 a 和 b 此外函数返回值也是 V 类型

我还是挺好奇如果传入的参数不是这五种会报什么错:

//go:build go1.18
// +build go1.18
package mainimport "fmt"func Sum2[V int64 | float64 | int32 | float32](a, b V) V {return a + b
}func main() {fmt.Println(Sum2[int](1, 2))
}

编译时报错:

# go1.18.1-beta/1.18-beta/generic/generic
.\main.go:20:21: int does not implement int64|float64|int32|float32

注意,在调用 Sum2 时,我们使用 [] 显示地制定了 Vint 类型,在编译器可以推断类型时,这个是可以省略的,也就是可以写作

func main() {fmt.Println(Sum2(1, 2))
}

但这并不是一直有用的,比如你要调用一个没有参数的泛型函数时,如:

func PI[V int | float64]() V {var v Vv = 10.0return v
}func main() {// fmt.Println(PI())  // .\main.go:28:16: cannot infer V fmt.Println(PI[float64]()) // 10
}

此外,都知道 go map 的 key 要求是可比较的类型,因此,go 新增了一个关键字 comparable 表明泛型是一个可比较类型, 当泛型参数作为 map 的 key 时,它必须是可比较的。

//go:build go1.18
// +build go1.18
package mainimport "fmt"func Sum[K comparable, V int64 | float64](m map[K]V) V {var sum Vfor k, v := range m {sum += vfmt.Println(k)}return sum
}func main() {fmt.Println(Sum(map[int64]float64{1: 2.3, 2: 3.3}))
}

是不是觉得每次 int | int64 | float64 | int32 | float32 写太麻烦了,确实,为此 go1.18 提供了泛型接口,你可以像定义接口一样定义一个泛型类型,就像:

type Number interface {int | int8 | int16 | int32 | int64 | float32 | float64
}

在这之后,你就可以使用 Number 来代替这一长串了

模糊测试

模糊测试 (fuzz testing, fuzzing)是一种软件测试技术。其核心思想是将自动或半自动生成的随机数据输入到一个程序中,并监视程序异常,如崩溃,断言(assertion)失败,以发现可能的程序错误,比如内存泄漏。模糊测试常常用于检测软件或计算机系统的安全漏洞。

—— wikipedia 模糊测试

可以看看官网的这个例子

func FuzzHex(f *testing.F) {for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {f.Add(seed)}f.Fuzz(func(t *testing.T, in []byte) {enc := hex.EncodeToString(in)out, err := hex.DecodeString(enc)if err != nil {t.Fatalf("%v: decode: %v", in, err)}if !bytes.Equal(in, out) {t.Fatalf("%v: not equal after round trip: %v", in, out)}})
}

运行 go test -fuzz=Fuzz 即可进行模糊测试,用法和普通测试差不多,如果有需要请移步官方文档

workspace mode

这是非常爽的一个功能,想想这样一个场景,为了方便测试,你需要要改某一个功能(有时可能只是一个数值),但这个功能是一个单独的模块,通过 mod 引入,所以你下载了这个包,并用 replace 将其替换成了本地的路径,就像:

module go1.18.1-betago 1.18replace (github.com/json-iterator/go => /usr/bin/go/json-iterator/go
)

然后你就可以开心的改本地的模块了,但问题在于你每次提交代码时都需要回滚改过的 go.mod 否则大家就都用不了了……

workspace mode 就是解决了这样的问题,它引入了一个 go.work 文件,你可以在项目目录下执行 go work init . 来生成它,需要注意的是 workspace mode 只能用在 goMod 中,所以目录下必须有 go.mod 才能生成 go.work, 刚生成的文件内容类似:

go 1.18use ./.

go.work 中我们可以使用 replace:

go 1.18use ./.replace (github.com/json-iterator/go => /usr/bin/go/json-iterator/go
)

go 会优先选择 go.work 中的模块,这样你把 go.work 加入 .gitignore 就可以舒服地改代码了

再看看上面的文件,事实上,在提案上,只有三个元素:

The go.work file has three directives: the go directive, the directory directive, and the replace directive.

在 beta 版中, directory 被改成了 use, 这三个元素的作用是:

  • go: 指明一个 go 版本
  • use: 将包含go.mod文件的目录的绝对或相对路径作为参数。路径的语法与replace指令中的目录替换相同。路径必须是包含go.mod文件的模块目录。go.work文件必须至少包含一个use指令。
use (./tools // golang.org/x/tools./mod   // golang.org/x/mod
)
  • replace: 与 go mod 中的一样

可以简单的理解为 go.work 声明了一个工作目录,这个目录下的成员由 use 声明,在工作目录下执行构建时,会优先使用工作目录下的组件。

看这个例子

cd ~/project/go-beta/work
mkdir a b c
cd a
go mod init github.com/520MianXiangDuiXiang520/a
cd ../b
go mod init github.com/520MianXiangDuiXiang520/b
cd ../c
go mod init c
cd ..
go mod init work

当 work 引用 a b 时,由于这两个项目在 github 上不存在,所以之前只能使用 replace:

module workgo 1.18replace (github.com/520MianXiangDuiXiang520/a => ./agithub.com/520MianXiangDuiXiang520/b => ./bc => ./c
)require (github.com/520MianXiangDuiXiang520/a v0.0.0-00010101000000-000000000000github.com/520MianXiangDuiXiang520/b v0.0.0-00010101000000-000000000000c v0.0.0-00010101000000-000000000000
)

使用 workspace mode 后:

cd ~/project/go-beta/work
go work init . ./a ./b ./c

go mod 中可以只写:

module workgo 1.18

因为他们在同一个工作目录下

基于寄存器的调用规约

在 go 1.17 时就针对 X86-64 的处理器增加了这个,据说函数调用性能能提斯 20%,现在拓展到了 arm64 和 PPC64 但我没有这种处理器的电脑,不过可以对比一下旧版的函数调用方式:

package mainfunc demo(a int64, b int32, c int16, d int8) (int64, int32, int16, int8) {a += 111b += 222c += 333d += 89return a, b, c, d
}func main() {demo(0, 0, 0, 0)
}

在 go 1.14 的环境下,将上面的代码编译并输出汇编代码如下:

go build -gcflags="-l -S" main.go
"".demo STEXT nosplit size=55 args=0x20 locals=0x00x0000 00000 (E:go汇编\01.go:3)  TEXT    "".demo(SB), NOSPLIT|ABIInternal, $0-320x0000 00000 (E:go汇编\01.go:3)  PCDATA  $0, $-20x0000 00000 (E:go汇编\01.go:3)  PCDATA  $1, $-20x0000 00000 (E:go汇编\01.go:3)  FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)0x0000 00000 (E:go汇编\01.go:3)  FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)0x0000 00000 (E:go汇编\01.go:3)  FUNCDATA        $2, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)0x0000 00000 (E:go汇编\01.go:4)  PCDATA  $0, $00x0000 00000 (E:go汇编\01.go:4)  PCDATA  $1, $00x0000 00000 (E:go汇编\01.go:4)  MOVQ    "".a+8(SP), AX0x0005 00005 (E:go汇编\01.go:4)  ADDQ    $111, AX0x0009 00009 (E:go汇编\01.go:8)  MOVQ    AX, "".~r4+24(SP)0x000e 00014 (E:go汇编\01.go:5)  MOVL    "".b+16(SP), AX0x0012 00018 (E:\桌面文件\笔记\Note\g o\go汇编\01.go:5)  ADDL    $222, AX0x0017 00023 (E:go汇编\01.go:8)  MOVL    AX, "".~r5+32(SP)0x001b 00027 (E:go汇编\01.go:6)  MOVWLZX "".c+20(SP), AX0x0020 00032 (E:go汇编\01.go:6)  ADDL    $333, AX0x0025 00037 (E:go汇编\01.go:8)  MOVW    AX, "".~r6+36(SP)0x002a 00042 (E:go汇编\01.go:7)  MOVBLZX "".d+22(SP), AX0x002f 00047 (E:go汇编\01.go:7)  ADDL    $89, AX0x0032 00050 (E:go汇编\01.go:8)  MOVB    AL, "".~r7+38(SP)0x0036 00054 (E:go汇编\01.go:8)  RET0x0000 48 8b 44 24 08 48 83 c0 6f 48 89 44 24 18 8b 44  H.D$.H..oH.D$..D0x0010 24 10 05 de 00 00 00 89 44 24 20 0f b7 44 24 14  $.......D$ ..D$.0x0020 05 4d 01 00 00 66 89 44 24 24 0f b6 44 24 16 83  .M...f.D$$..D$..0x0030 c0 59 88 44 24 26 c3                             .Y.D$&.

1.18 编译结果如下:

# command-line-arguments
"".demo STEXT nosplit size=20 args=0x10 locals=0x0 funcid=0x0 align=0x00x0000 00000 (E:\add.go:3)      TEXT    "".demo(SB), NOSPLIT|ABIInternal, $0-160x0000 00000 (E:\add.go:3)      FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)0x0000 00000 (E:\add.go:3)      FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)0x0000 00000 (E:\add.go:3)      FUNCDATA        $5, "".demo.arginfo1(SB)0x0000 00000 (E:\add.go:3)      FUNCDATA        $6, "".demo.argliveinfo(SB)0x0000 00000 (E:\add.go:3)      PCDATA  $3, $10x0000 00000 (E:\add.go:4)      ADDQ    $111, AX0x0004 00004 (E:\add.go:5)      ADDL    $222, BX0x000a 00010 (E:\add.go:6)      ADDL    $333, CX0x0010 00016 (E:\add.go:7)      ADDL    $89, DI0x0013 00019 (E:\add.go:8)      RET0x0000 48 83 c0 6f 81 c3 de 00 00 00 81 c1 4d 01 00 00  H..o........M...0x0010 83 c7 59 c3

结果一目了然吧,两个都开了编译优化 -N 1.14 用的完全是栈, 1.18 用了四个寄存器: AX BX CX DI,那最多会用多少个寄存器呢?

# command-line-arguments
"".demo STEXT nosplit size=66 args=0x48 locals=0x0 funcid=0x0 align=0x00x0000 00000 (E:\add.go:3)      TEXT    "".demo(SB), NOSPLIT|ABIInternal, $0-720x0000 00000 (E:\add.go:3)      FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)0x0000 00000 (E:\add.go:3)      FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)0x0000 00000 (E:\add.go:3)      FUNCDATA        $5, "".demo.arginfo1(SB)0x0000 00000 (E:\add.go:3)      FUNCDATA        $6, "".demo.argliveinfo(SB)0x0000 00000 (E:\add.go:3)      PCDATA  $3, $10x0000 00000 (E:\add.go:14)     MOVQ    "".j+8(SP), DX0x0005 00005 (E:\add.go:14)     ADDQ    $787, DX0x000c 00012 (E:\add.go:16)     MOVQ    DX, "".~r9+16(SP)0x0011 00017 (E:\add.go:5)      ADDQ    $111, AX0x0015 00021 (E:\add.go:6)      ADDL    $222, BX0x001b 00027 (E:\add.go:7)      ADDL    $333, CX0x0021 00033 (E:\add.go:8)      ADDL    $89, DI0x0024 00036 (E:\add.go:9)      ADDQ    $99, SI0x0028 00040 (E:\add.go:10)     ADDQ    $88, R80x002c 00044 (E:\add.go:11)     ADDQ    $999, R90x0033 00051 (E:\add.go:12)     ADDQ    $898, R100x003a 00058 (E:\add.go:13)     ADDQ    $989, R110x0041 00065 (E:\add.go:16)     RET0x0000 48 8b 54 24 08 48 81 c2 13 03 00 00 48 89 54 24  H.T$.H......H.T$0x0010 10 48 83 c0 6f 81 c3 de 00 00 00 81 c1 4d 01 00  .H..o........M..0x0020 00 83 c7 59 48 83 c6 63 49 83 c0 58 49 81 c1 e7  ...YH..cI..XI...0x0030 03 00 00 49 81 c2 82 03 00 00 49 81 c3 dd 03 00  ...I......I.....0x0040 00 c3

答案是 9 个 超出的部分会按顺序放在栈上

go version

这个指令最基本的用法是查看 go 版本

E:\1.18-beta\as>go version
go version go1.18beta1 windows/amd64

但其实它还可以看 go 编译产物的构建版本信息,这次增加了一个 -m 参数:

E:\1.18-beta\as>go version -m add.exe
add.exe: go1.18beta1path    command-line-argumentsbuild   -compiler=gcbuild   -gcflags=-l -Sbuild   CGO_ENABLED=1build   CGO_CFLAGS=build   CGO_CPPFLAGS=build   CGO_CXXFLAGS=build   CGO_LDFLAGS=build   GOARCH=amd64build   GOOS=windowsbuild   GOAMD64=v1

参考

Go 1.18 Beta 1 is available, with generics

Tutorial: Getting started with generics

pkg.go.dev#Fuzzing

Proposal: Multi-Module Workspaces in cmd/go

Go 1.18.1 Beta 尝鲜 泛型 FuzzTest workspace mode相关推荐

  1. 前端每周清单第 49 期:Webpack 4 Beta 尝鲜,React Windowing 与 setState 分析

    前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点:分为新闻热点.开发教程.工程实践.深度阅读.开源项目.巅峰人生等栏目.欢迎关注[前端之巅]微信公众号(ID: fron ...

  2. JetBrains开发者日见闻(一)之Kotlin/Native 尝鲜篇

    简述: 今天我们来讲点Kotlin中比较时髦的东西,有的人可能会说:"不像你之前的风格啊,之前的文章不是一直在死扣语法以及语法糖背后秘密.当你还在死扣泛型语法的时候,别人的文章早就说了Kot ...

  3. 华为鸿蒙去哪里更新,华为鸿蒙OS正式尝鲜版名单更新,升级?还是不升级?

    原标题:华为鸿蒙OS正式尝鲜版名单更新,升级?还是不升级? 请点击[关注],获取更多数码资讯 华为鸿蒙系统正式版本已经开始推送,公测尝鲜版与正式尝鲜版机型名单已经更新:"Mate40系列.M ...

  4. 华为鸿蒙系统如何申请尝鲜,鸿蒙OS 2.0公测尝鲜来咯

    首先说一下6月2号鸿蒙OS将正式发布! 其次根据鸿蒙技术社区消息,鸿蒙 OS 首批用户尝鲜计划开启咯,Beta 尝鲜最低支持Mate20 系列手机,mate20系列之后的华为 Mate/novaico ...

  5. 华为鸿蒙公测尝鲜,华为鸿蒙尝鲜测试正式开始,分三种形式

    描述 近日,鸿蒙尝鲜/测试,正式开始,"我的华为"APP 中公布的鸿蒙系统,华为机型部分名单,操作步骤:打开"我的华为"→搜索鸿蒙测试即可. 鸿蒙尝鲜可以申请, ...

  6. 鸿蒙开放机型,鸿蒙今天对友商打响了第一炮!华为多款机型开启鸿蒙尝鲜

    鸿蒙尝鲜可以申请,目前分为三种尝鲜形式和适配机型如下: 1.Beta尝鲜:Mate Xs.Mate 20系列.nova 8 系列.nova 7系列.nova 6系列.MatePad.MatePad 1 ...

  7. 华为鸿蒙系统公测机型,鸿蒙OS尝鲜入口提前开放首批可更新机型可在“我的华为”看到惊喜...

    随着距离6月2日的鸿蒙操作系统及华为全场景新品发布会进入倒计时1天,关于更新鸿蒙系统的机型的信息也越来越详细.继此前已经曝光的首批支持鸿蒙系统尝鲜的机型名单之外,日前有小伙伴发现,首批尝鲜机型的鸿蒙O ...

  8. 鸿蒙华为测试,华为鸿蒙尝鲜测试正式开始,分三种形式

    近日,鸿蒙尝鲜/测试,正式开始,"我的华为"APP 中公布的鸿蒙系统,华为机型部分名单,操作步骤:打开"我的华为"→搜索鸿蒙测试即可. 鸿蒙尝鲜可以申请,目前分 ...

  9. 华为鸿蒙os正式亮相,华为鸿蒙OS正式亮相,9月10日P30系列尝鲜EMUI10系统!

    原标题:华为鸿蒙OS正式亮相,9月10日P30系列尝鲜EMUI10系统! 华为开发者大会在华为东莞松山湖基地拉开帷幕.在下午的主题演讲中,华为鸿蒙OS及EMUI10正式发布. 鸿蒙OS 华为消费者业务 ...

最新文章

  1. python第三方库之学习pyserial库--串口通信
  2. mfc 应用程序 语言进行本地化
  3. Spark SQL入门示例
  4. Martix工作室考核题 —— 输出1000以内能被3整除,且个位数为6的所有整数。
  5. linux下oracle导出12705,llinux下Oracle数据库的单个表导出导入
  6. 常见的 Error 和 Exception
  7. ajax和fetch哪个好,axios和ajax,fetch的区别
  8. 压缩之后神经网络忘记了什么?Google研究员给出了答案
  9. 【Elasticsearch】Elasticsearch自定义评分的N种方法
  10. DBUtils结果集处理
  11. 允许保存文件 html5,html5 保存文件
  12. Ros简单程序编写及使用类Hello World
  13. laravel mysql 悲观锁_Laravel中悲观锁 乐观锁的使用
  14. 《Spring揭秘》学习部分总结
  15. android经典动态壁纸,Android动态壁纸解析
  16. java如何调用webservice_Java如何调用WebService接口
  17. 多种前端框架的优缺点
  18. 2019-05-22 SQL注入;啊D注入工具;
  19. 走进小作坊(八)----公益之痒
  20. jQuery封装的获取Url中的Get参数

热门文章

  1. css background-attachment:fixed 固定背景、不随内容一起滚动
  2. IntelliJ IDEA编码设置
  3. HTTP 协议(详解)
  4. 十天学Linux内核之第七天---电源开和关时都发生了什么
  5. 转载Linq中GroupBy方法的使用总结
  6. Linux删除特殊字符文件
  7. Redis3.2.5部署(单节点)
  8. 算法题:判断字符串是否为 ipv4 地址
  9. Fragment的保存
  10. Linux基础(九)--定时任务