Go2 新特性简明教程

quick-go2

udemy.com

Go 的演进

Go语言/golang 诞生于2007年,经过12年的发展,Go逐渐成为了云计算领域新一代的开发语言。Go语言在牺牲很少性能的情况下,语法简洁,功能强大。我是Python的重度用户,在学习Go时,却有一种在学习Python的感觉。并非语法相似,而是Go语言作为一门编译型语言,竟然能够像Python一样,少量的代码就能够完成尽可能多的事情。Go语言仿佛是C和Python的结合体。

GoroutineChannelDocker

Go语言也有很多令人诟病的地方,例如包管理机制,Go直到v1.6才默认开启了vendor机制,vendor机制非常简陋,简单说就是在项目目录下增加一个vendor文件夹,里面放第三方依赖。vendor机制是没有版本概念的,而且不能解决vendor目录嵌套的问题以及同名包函数冲突问题。后来社区涌现了大量的包管理工具,仅官方推荐的包管理工具就有15种之多,应用比较广泛的,如dep、govendor。直到v1.11,官方增加了Go modules机制,才算较为完整地解决了包管理的问题。

Go2 可以说是Go语言一个非常重要的里程碑,Go1 目前虽然已经到了1.12版本,事实上每一个版本很少涉及语法层面的变化,而且每个版本都是向前兼容的。较大的改动如下:

  • Go1.2 切片操作
1
2
var a = make([]int, 10)
var b = a[i:j:k]
  • Go1.4 for语言加强
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// <= 1.3
for i, v := range x {
// ...
}

for i := range x {
// ...
}

// 1.4 新增
var times [5][0]int

for i := 0; i < len(times); i++ {
// ...
}

for _ = range times {
// ...
}
  • Go1.9 类型别名
1
type T1 = T2

Go 2 设计草案

错误处理(Error handling)错误值(Error values)泛型(Generics)

错误处理(Error Handling)

if
1
2
3
4
5
6
7
8
func CopyFile(src, dst string) {
r := os.Open(src)
defer r.Close()

w := os.Create(dst)
io.Copy(w, r)
w.Close()
}

IO操作容易引发错误,文件打开失败,创建失败,拷贝失败等都会产生错误。如果要对这个函数进行完整的错误处理,代码将变成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func CopyFile(src, dst string) error {
r, err := os.Open(src)
if err != nil {
return err
}
defer r.Close()

w, err := os.Create(dst)
if err != nil {
return err
}
defer w.Close()

if _, err := io.Copy(w, r); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
}

看似逻辑清晰,但不够优雅,充斥了大量重复的逻辑。这是Go错误处理机制的缺陷。同时,因为错误处理机制的繁琐,很多开发者在开发应用时,很少去检查并处理错误,程序的健壮性得不到保证。

为了解决这个问题,Go2 发布了一个设计草案供社区讨论,Go2将会完善错误处理机制,错误处理的语法将会简洁很多。

handle errcheck
1
2
3
4
5
6
7
8
9
10
11
func CopyFile(src, dst string) error {
handle err {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
r := check os.Open(src)
defer r.Close()

w := check os.Create(dst)
check io.Copy(w, r)
check w.Close()
}
try
1
data := try parseHexdump(string(hex))
try
1
2
3
4
5
data, err := parseHexdump(string(hex))
if err == ErrBadHex {
... special handling ...
}
try err
check errtry err

错误值(Error values)

Error valueserrorio.EOFos.IsNotExisterr.Error()
1
2
3
4
5
6
7
8
9
10
11
func funcB() error {
if v, err := funcA(); if err != nil {
return fmt.Errorf("connect to db: %v", err)
}
}
func funcC() error {
v, err := funcB()
if err != nil {
return fmt.Errorf("write users database: %v", err)
}
}
funcC
1
write users database: connect to db: open /etc/xx.conf: permission denied

每一层,用额外的字符串对错误进行封装,是目前最常用的方法,除了通过字符串解析,很难还原出完整的错误链条。

Error inspectionError formatting
Unwrap
1
2
3
4
5
package errors

type Wrapper interface {
Unwrap() error
}

例如,

1
2
// WriteError 实现 Unwrap 接口
func (e *WriteError) Unwrap() error { return e.Err }
Format
1
2
3
4
5
package errors

type Formatter interface {
Format(p Printer) (next error)
}

例如,

1
2
3
4
5
6
7
func (e *WriteError) Format(p errors.Printer) (next error) {
p.Printf("write %s database", e.Database)
if p.Detail() {
p.Printf("more detail here")
}
return e.Err
}

泛型(Generics)

inferface{}
1
2
3
4
5
6
7
8
9
type List(type T) []T
// 返回map的键
func Keys(type K, V)(m map[K]V) []K
// 去重过滤
func Uniq(<-chan T) <-chan T
// 合并
func Merge(chans ...<-chan T) <-chan T
// 使用自定义排序函数排序
func SortSlice(data []T, less func(x, y T) bool)

例如,我们需要返回一个map对象中所有的键,而希望这个键的类型可以是任意类型。

1
2
3
4
var ints List(int)
keysA := Keys(int, string)(map[int]string{1:"one", 2: "two"})
keysB := Keys(string, string)(map[string]string{"name":"geektutu", "age": "twenty"})
// [1, 2]

Go 2 新特性

Go2还未正式发布,发布后更新

上一篇 « 博客折腾记(六) - 不要为了流量忘记了初心 下一篇 » Go语言动手写Web框架 - Gee第二天 上下文Context