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