Go 语言错误处理——改进版(Must 模式)

问题:冗余的错误检查

在 Go 语言中,通常错误是返回值的一部分。这意味着通常在调用函数的地方要立即处理,通常的做法是返回一个对象,并检查其是否为nil:error nil.

result, err := someFunction()
if err != nil {
    return err
}
  • 处理缺乏灵活性: 由于 Go 语言没有异常机制,仅依靠返回错误值来处理错误,因此每个错误都需要在当前代码逻辑中进行处理,或者沿着调用栈向上返回。这种处理方式使得开发人员在将错误处理逻辑与主要应用逻辑分离时灵活性欠佳,会让人感觉受到限制,并且容易导致错综复杂的错误检查链。
  • 维护开销: 当代码重复性很高时,微小的改动就需要在多个地方进行修改。在某一处更新错误处理逻辑可能意味着要在调用栈中所有使用了类似检查的地方都进行更新,这使得保持代码库的一致性和无差错变得十分困难。

为何 Go 语言采用这种错误处理方式

Go 语言的创造者之所以选择这种将错误视为值的处理方式,是为了提升简洁性和明确性。通过在每次函数调用后检查错误,Go 语言确保错误不会被忽视。该语言重视显式处理而非隐式处理,这种处理方式有助于避免隐藏的漏洞或未处理的异常情况。

错误处理冗余的示例

设想这样一个场景:你要按顺序进行多次应用程序编程接口(API)调用。

user, err := getUser(id)
if err != nil {
    return err
}

posts, err := getUserPosts(user.ID)
if err != nil {
    return err
}
comments, err := getComments(posts[0].ID)
if err != nil {
    return err
}

步骤 1:在utils.go文件中定义 “Must” 函数

该函数将接收一个泛型类型和一个错误(error)。如果错误为nil,它将返回对应的值;否则,它将触发异常(panic)。这个工具函数通过将重复的检查集中起来,简化了错误处理操作。

// utils.go
package utils

// Must is a helper function that takes a value of any type and an error.
// If the error is nil, it returns the value; if the error is non-nil, it panics.
func Must[T any](value T, err error) T {
    if err != nil {
        panic(err)
    }
    return value
}

步骤 2:在你的代码中使用 “Must” 函数

使用 “Must” 函数能够让你精简函数调用,并以一种更简洁的方式处理错误。以下是一个在代码中如何使用它的示例:

package main

import (
    "fmt"
    "must_example/utils"
)

func main() {
    //NO NEED TO WRITE ERROR CHECKS AGAIN AND AGAIN 🎉
    user := utils.Must(getUser(1))
    posts := utils.Must(getUserPosts(1))
    comments := utils.Must(getComments(1))

    fmt.Println("User:", user)
    fmt.Println("Posts:", posts)
    fmt.Println("Comments:", comments)
}

为何采用这种方式?

  • 减少冗余: 不必在每次调用后都重复编写 if err!= nil 这样的错误检查语句,Must 函数将这类检查集中了起来。
  • 可读性: 主要逻辑不会因错误检查而变得杂乱,使其更易于理解。
  • 快速调试: Must 函数会立即触发异常,这在错误属于意外情况或者应当停止执行的情形下很有用处。

使用 Must 函数的注意事项

虽然 Must 函数提高了可读性,但谨慎使用它至关重要:

  • 生产环境代码: 对于那些预期会出现错误且应当妥善处理错误的代码,要避免使用 Must 函数。

注意: “Must” 函数在某些场景下可能是合适的,比如命令行界面(CLI)应用程序,在这类应用中,故障处理通常可以交由顶层的主函数来负责。

结论

使用 “Must” 函数对错误处理进行简化是朝着让 Go 语言代码更简洁、更易于维护迈出的强有力的一步。这样做能够减少冗余,让逻辑更清晰,进而提高代码的可读性和效率,对任何 Go 语言程序员来说这显然是有益的。像这样的工具和模式是助力你在成为更高效的 Go 语言开发者之路上不断成长的最佳方式之一,能让你直面挑战,编写出更好、更周全的代码。

原文地址: https://medium.com/@kartik11buttan/golang-error-handling-improvised-must-pattern-d867dc09c646