• 4.3. 尽早 return 而不是深度嵌套

    4.3. 尽早 return 而不是深度嵌套

    由于 Go 语言的控制流不使用 exception,因此不需要为 trycatch 块提供顶级结构而深度缩进代码。Go 语言代码不是成功的路径越来越深地嵌套到右边,而是以一种风格编写,其中随着函数的进行,成功路径继续沿着屏幕向下移动。 我的朋友 Mat Ryer 将这种做法称为“视线”编码。[4]

    这是通过使用 guard clauses 来实现的; 在进入函数时是具有断言前提条件的条件块。 这是一个来自 bytes 包的例子:

    1. func (b *Buffer) UnreadRune() error {
    2. if b.lastRead <= opInvalid {
    3. return errors.New("bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune")
    4. }
    5. if b.off >= int(b.lastRead) {
    6. b.off -= int(b.lastRead)
    7. }
    8. b.lastRead = opInvalid
    9. return nil
    10. }

    进入 UnreadRune 后,将检查 b.lastRead 的状态,如果之前的操作不是 ReadRune,则会立即返回错误。 之后,函数的其余部分继续进行 b.lastRead 大于 opInvalid 的断言。

    与没有 guard clause 的相同函数进行比较,

    1. func (b *Buffer) UnreadRune() error {
    2. if b.lastRead > opInvalid {
    3. if b.off >= int(b.lastRead) {
    4. b.off -= int(b.lastRead)
    5. }
    6. b.lastRead = opInvalid
    7. return nil
    8. }
    9. return errors.New("bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune")
    10. }

    最常见的执行成功的情况是嵌套在第一个if条件内,成功的退出条件是 return nil,而且必须通过仔细匹配大括号来发现。 函数的最后一行是返回一个错误,并且被调用者必须追溯到匹配的左括号,以了解何时执行到此点。

    对于读者和维护程序员来说,这更容易出错,因此 Go 语言更喜欢使用 guard clauses 并尽早返回错误。