Go 2的总体目标是在辅助工程扩展为大的代码基线时做到游刃有余。 通常,我们的 Go 程序有很多错误检查,但缺少错误处理。我们通常使用如下代码所示的赋值判断语句进行错误检查。
if _, err := io.Copy(w, r); nil != err {
return err
}
这样写起来较繁琐,设计草案旨在引入一种轻量的语法来进行错误检查以解决当前的这些问题。
1 当前问题
Go 使用的是对显式错误结果的显式错误检查,而其他异常处理型语言(诸如C++,C#,Java等)使用的是对隐式结果进行隐式检查。 对于异常处理型语言的处理方式,因我们全然看不到隐式检查,所以难以验证程序是否正确恢复到检查失败时的状态。 下面是一个错误检查较完整的文件拷贝代码,其错误处理的重点在于当io.Copy或w.Close失败时,应移除写了一半的dst文件。
func CopyFile(src, dst string) error {
r, err := os.Open(src)
if err != nil {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
if _, err := io.Copy(w, r); err != nil {
w.Close()
os.Remove(dst)
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
if err := w.Close(); err != nil {
os.Remove(dst)
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
}
该代码较健壮,但不够整洁,也不够优雅。
2 目标
减少大量错误检查代码,使错误检查更轻量,使错误处理更便捷。 不重蹈异常处理的覆辙,错误检查及错误处理应继续保持显式的方式。 兼容现有代码。
3 草案概览
设计草案引入了两个新的关键字,check与handle,分别进行错误检查与错误处理。 使用check f(x, y, z)或check err进行显式错误检查。 使用hande语句进行错误处理器的定义。 当错误检查失败时,其转向到最里边的Handler,最里边的Handler又转向到其上的下一个Handler,直至某一个Handler执行了return语句。 例如,依照设计草案,如上代码可以改进为更简短的方式:
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)
handle err {
w.Close()
os.Remove(dst) // (only if a check fails)
}
check io.Copy(w, r)
check w.Close()
return nil
}
4 草案详情
v1, ..., vN := check /expr/其等价于:
v1, ..., vN, vErr := /expr/
if vErr != nil {
/error result/ = handlerChain(vn)
return
}
vErr必须为error类型。
类似,foo(check /expr/)等价于:
v1, ..., vN, vErr := /expr/
if vErr != nil {
/error result/ = handlerChain(vn)
return
}
foo(v1, ..., vN)
如下是一段常规的错误处理代码:
func printSum(a, b string) error {
x, err := strconv.Atoi(a)
if err != nil {
return err
}
y, err := strconv.Atoi(b)
if err != nil {
return err
}
fmt.Println("result:", x + y)
return nil
}
其可被改写为:
func printSum(a, b string) error {
handle err { return err }
fmt.Println("result:", check strconv.Atoi(x) + check strconv.Atoi(y))
return nil
}
通常需要包装下错误信息的上下文,代码可以写作:
func printSum(a, b string) error {
handle err {
return fmt.Errorf("printSum(%q + %q): %v", a, b, err)
}
fmt.Println("result:", check strconv.Atoi(x) + check strconv.Atoi(y))
return nil
}
func process(user string, files chan string) (n int, err error) {
handle err { return 0, fmt.Errorf("process: %v", err) } // handler A
for i := 0; i < 3; i++ {
handle err { err = fmt.Errorf("attempt %d: %v", i, err) } // handler B
handle err { err = moreWrapping(err) } // handler C
check do(something()) // check 1: handler chain C, B, A
}
check do(somethingElse()) // check 2: handler chain A
}
Check 1:在循环内,依序运行Handler C、B及A。不同于defer,定义在循环内的Handler不会因每次新的迭代而累积。
Check 2:在函数末尾,仅运行Handler A。
几个重要点:
check到错误,即会落入Handler,无法再回到对应函数的控制;
Handler执行总是在defer语句之前;
若对应函数需要有返回值,但check的Handler链函数没有return语句会引起编译错误。
func printSum(a, b string) error {
x := check strconv.Atoi(a)
y := check strconv.Atoi(b)
fmt.Println("result:", x + y)
return nil
}
入群交流(该群和以上内容无关):Go中文网 QQ交流群:731990104 或 加微信入微信群:274768166 备注:入群; 公众号:Go语言中文网
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Web Designer's Idea Book, Vol. 2
Patrick McNeil / How / 2010-9-19 / USD 30.00
Web Design Inspiration at a Glance Volume 2 of The Web Designer's Idea Book includes more than 650 new websites arranged thematically, so you can easily find inspiration for your work. Auth......一起来看看 《The Web Designer's Idea Book, Vol. 2》 这本书的介绍吧!