4.3. 尽早 return
而不是深度嵌套
由于 Go 语言的控制流不使用 exception
,因此不需要为 try
和 catch
块提供顶级结构而深度缩进代码。Go 语言代码不是成功的路径越来越深地嵌套到右边,而是以一种风格编写,其中随着函数的进行,成功路径继续沿着屏幕向下移动。 我的朋友 Mat Ryer 将这种做法称为“视线”编码。[4]
这是通过使用 guard clauses
来实现的; 在进入函数时是具有断言前提条件的条件块。 这是一个来自 bytes
包的例子:
func (b *Buffer) UnreadRune() error {
if b.lastRead <= opInvalid {
return errors.New("bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune")
}
if b.off >= int(b.lastRead) {
b.off -= int(b.lastRead)
}
b.lastRead = opInvalid
return nil
}
进入 UnreadRune
后,将检查 b.lastRead
的状态,如果之前的操作不是 ReadRune
,则会立即返回错误。 之后,函数的其余部分继续进行 b.lastRead
大于 opInvalid
的断言。
与没有 guard clause
的相同函数进行比较,
func (b *Buffer) UnreadRune() error {
if b.lastRead > opInvalid {
if b.off >= int(b.lastRead) {
b.off -= int(b.lastRead)
}
b.lastRead = opInvalid
return nil
}
return errors.New("bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune")
}
最常见的执行成功的情况是嵌套在第一个if条件内,成功的退出条件是 return nil
,而且必须通过仔细匹配大括号来发现。 函数的最后一行是返回一个错误,并且被调用者必须追溯到匹配的左括号,以了解何时执行到此点。
对于读者和维护程序员来说,这更容易出错,因此 Go 语言更喜欢使用 guard clauses
并尽早返回错误。