首页 > 其他分享 >Go语言精进之路读书笔记第38条——尽量优化反复出现的if err != nil

Go语言精进之路读书笔记第38条——尽量优化反复出现的if err != nil

时间:2024-02-28 21:13:40浏览次数:15  
标签:38 return err nil 读书笔记 dst func os

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

相关文章

  • yolo7检测学习Bubbliiiing的视频有感——(2)FileNotFoundError: [Errno 2] No such file
    这个问题作为老程序员是不应该犯的,因为只是相对路径和绝对路径的问题按照步骤将对应的数据集放入目录后,运行voc_annotation.py想要生成两个txt文件,结果发现报错FileNotFoundError:[Errno2]Nosuchfileordirectory其实就是classes_path和VOCdevkit_path的路径不对,像我自己......
  • 383. 赎金信C
    \inthash(charc){returnc-'a';}boolcanConstruct(char*ransomNote,char*magazine){if(!ransomNote)returntrue;if(!magazine)returnfalse;inta[26]={0};intb[26]={0};inti=0,j=0;while(ransomNote[i]!=0){......
  • 《构建之法》读书笔记
    构建之法读书心得体会读完《构建之法》第一章后,我对软件开发中的各种问题和挑战有了更深入的理解。这本书以其独特的视角,清晰的分析和实用的建议,使我重新审视了软件开发的过程和方法。以下是我的主要心得体会:1.**理解复杂性**:书中强调了理解复杂性在软件开发中的重要性。我们不......
  • 《构建之法》读书笔记——什么是好的软件
    “什么是好的软件?一些同学认为,所谓好软件,就是软件没有缺陷(Bug),所谓软件工程,就是把软件中的Bug都消灭掉的过程。这的确是抓住了软件工程的一个要素。和软件打交道的专业人士都知道软件有“Bug”(缺陷),软件团队的很多人都整天和Bug打交道,Bug的多少可以直接衡量一个软件的开发效率、用户......
  • 《大道至简》读书笔记第四章
    《大道至简》第四章着重强调简约之道。通过阅读这一章,我深刻体会到在软件工程实践中,简约是一种重要的美德,同时也是一种高效的工作方式。作者指出,软件工程实践者应当追求简单和直接的解决方案,避免过度复杂和繁琐的设计。简约的设计不仅能够提高代码的可读性和可维护性,还能够减少错......
  • 《大道至简》第六章读书笔记
    《大道至简》第六章深入探讨了软件工程实践者在追求简洁和高效的同时,如何保持和提升自身技术能力的思想。通过阅读这一章,我进一步认识到在软件工程领域,持续学习和技术提升是非常重要的。作者指出,软件工程实践者应当保持对新技术和新工具的持续学习和掌握,不断提升自己的技术能力和......
  • 《大道至简》第五章读书笔记
    《大道至简》第五章探讨了软件工程实践者在面对挑战和困难时如何应对的思想。通过阅读这一章,我深刻领悟到在软件开发过程中,坚持不懈、勇敢面对问题、勇于创新是取得成功的重要因素。作者强调了在面对困难和挑战时,软件工程师要保持乐观积极的态度,勇于面对问题并积极寻找解决方案。......
  • 对 vCenter Server 中的性能数据间断或性能数据缺失进行故障排除 (1003878)
    SymptomsGapsinperformancedataMissingperformancedata ResolutionValidatethateachtroubleshootingstepbelowistrueforyourenvironment.Eachstepprovidesinstructionsoralinktoadocument,inordertoeliminatepossiblecausesa......
  • ABC338G evall 题解
    题意:给定一个由数字和加号和乘号组成的字符串,求出\(\sums(i,j)\)。其中\(s(i,j)\)表示\(i\)到\(j\)字符组成的表达式的值。考虑\(\text{dp}\)。设\(dp_{i}\)表示以\(i\)为起点的所有表达式的值之和。那么我们考虑以一些加号作为分界点来转移。假设\(i\)右边最......
  • IIS部署 HTTP 错误 500.19 - Internal Server Error 解决步骤 由于权限不足而无法
    配置应用程序池的身份 调整文件和文件夹权限确保应用程序池的身份有权访问网站的目录和文件在网站中进行配置1.添加2.高级3.立即查找找到所需身份名称后添加权限 至少需要读取权限,如果应用程序需要写入文件或目录,则还需要写入权限 ......