Go在最初设计时就有意识地选择了使用显式错误结果和显式错误检查
38.1 两种观点
显式的错误处理方式让Go程序员首先考虑失败情况,这将引导Go程序员在编写代码时处理故障,而不是在程序部署并运行在生产环境后再处理。而为反复出现的代码片段if err != nil {...}
所付出的成本已基本被在故障发生时处理故障的成本超过。
try提案:与Go完成一件事情仅有一种方法的原则相背离,有的人使用新引入的try,有的人则依旧喜欢使用传统的if err != nil
的错误检查。
38.2 尽量优化
可以通过良好的设计减少或消除这类反复出现的错误检查。
38.3 优化思路
(1) 改善代码的视觉呈现
try提案(已经被否决)
(2) 降低if err != nil 重复的次数
利用圈复杂度(Cyclomatic complexity)来进行衡量
1.视觉扁平化
将触发错误处理的语句与错误处理代码放在一行
if _, err = io.Copy(w, r); err != nil {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
2.重构:减少if err != nil
的重复次数
增加中间层,将大函数拆分成小函数,降低圈复杂度,拆分后每个函数的if err != nil
重复次数都在可接受范围内
func openBoth(src, dst string) (*os.File, *os.File, error) {
var r, w *os.File
var err error
if r, err = os.Open(src); err != nil {
return nil, nil, fmt.Errorf("copy %s %s: %v", src, dst, err)
}
if w, err = os.Create(dst); err != nil {
r.Close()
return nil, nil, fmt.Errorf("copy %s %s: %v", src, dst, err)
}
return r, w, nil
}
func CopyFile(src, dst string) error {
var err error
var r, w *os.File
if r, w, err = openBoth(src, dst); err != nil {
return err
}
defer func() {
r.Close()
w.Close()
if err != nil {
os.Remove(dst)
}
}()
if _, err = io.Copy(w, r); err != nil {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
return nil
}
func main() {
err := CopyFile("foo.txt", "bar.txt")
if err != nil {
fmt.Println("copyfile error:", err)
return
}
fmt.Println("copyfile ok")
}
3.check / handle 风格化(不推荐)
业务逻辑中不建议使用panic,panic和recover让函数调用的性能下降了约90%
4.封装:内置error状态 (推荐)
在“Errors are values”一文中,Rob Pike为我们呈现了在Go标准库中使用了避免if err != nil
反复出现的一种代码设计思路。
bufio包的Writer就是使用这个思路实现的,因此它可以像下面这样使用:
b := bufio.NewWriter(fd)
b.Write(p0[a:b])
b.Write(p1[c:d])
b.Write(p2[e:f])
if b.Flush() != nil {
return b.Flush()
}
上述代码中并没有判断三个b.Write的返回错误值,因为错误状态被封装在bufio.Writer结构的内部了,Writer定义了一个err字段作为内部错误状态值,它与Writer的实例绑定在了一起,并且在Write方法的入口判断是否为nil。一旦不为nil,Write什么都不做就会返回
// $GOROOT/src/bufio/bufio.go
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}
func (b *Writer) Write(p []byte) (nn int, err error) {
for len(p) > b.Available() && b.err == nil {
...
}
if b.err != nil {
return nn, b.err
}
......
return nn, nil
}
参考bufio包的思路,优化之前的代码
type FileCopier struct {
w *os.File
r *os.File
err error
}
func (f *FileCopier) open(path string) (*os.File, error) {
if f.err != nil {
return nil, f.err
}
h, err := os.Open(path)
if err != nil {
f.err = err
return nil, err
}
return h, nil
}
func (f *FileCopier) openSrc(path string) {
if f.err != nil {
return
}
f.r, f.err = f.open(path)
return
}
func (f *FileCopier) createDst(path string) {
if f.err != nil {
return
}
f.w, f.err = os.Create(path)
return
}
func (f *FileCopier) copy() {
if f.err != nil {
return
}
if _, err := io.Copy(f.w, f.r); err != nil {
f.err = err
}
}
func (f *FileCopier) CopyFile(src, dst string) error {
if f.err != nil {
return f.err
}
defer func() {
if f.r != nil {
f.r.Close()
}
if f.w != nil {
f.w.Close()
}
if f.err != nil {
if f.w != nil {
os.Remove(dst)
}
}
}()
f.openSrc(src)
f.createDst(dst)
f.copy()
return f.err
}
func main() {
var fc FileCopier
err := fc.CopyFile("foo.txt", "bar.txt")
if err != nil {
fmt.Println("copy file error:", err)
return
}
fmt.Println("copy file ok")
}
标签:38,return,err,nil,读书笔记,dst,func,os
From: https://www.cnblogs.com/brynchen/p/18041827