Golang的错误处理
#编程 #go
与其他一些编程语言相比,GO中的错误处理在某种程度上是独一无二的。 GO不使用异常,而是使用错误值表示异常状态。此设计促进了明确的错误检查并可能导致更强大的代码。

1. error类型:

go具有一个称为error的内置接口类型。该界面具有单个方法Error() string

type error interface {
    Error() string
}

任何实现此方法的类型都可以用作错误。

2.返回错误:

这是惯用函数和方法返回错误的惯用性。一个常见的模式是返回结果和错误,并立即检查错误:

func SomeFunction() (ResultType, error) {
    if somethingWrong {
        return nil, errors.New("something went wrong")
    }
    return result, nil
}

result, err := SomeFunction()
if err != nil {
    // handle the error
}

3.自定义错误类型:

自定义错误类型当您想以允许呼叫者对不同错误方案做出反应的方式提供有关错误或对错误进行分类的更多信息时,可能特别有用。

这是您如何在GO中使用自定义错误类型的示例:

package main

import (
    "fmt"
)

// NotFoundError represents an error when something is not found.
type NotFoundError struct {
    EntityName string
}

func (e *NotFoundError) Error() string {
    return fmt.Sprintf("%s not found", e.EntityName)
}

// PermissionError represents an error when permissions are denied.
type PermissionError struct {
    OperationName string
}

func (e *PermissionError) Error() string {
    return fmt.Sprintf("permission denied for %s", e.OperationName)
}

使用和检查自定义错误类型:

func FindEntity(name string) (string, error) {
    // Simulating some logic
    if name == "missingEntity" {
        return "", &NotFoundError{EntityName: name}
    }
    return "EntityData", nil
}

func PerformOperation(name string) error {
    // Simulating some logic
    if name == "restrictedOperation" {
        return &PermissionError{OperationName: name}
    }
    return nil
}

func main() {
    _, err := FindEntity("missingEntity")
    if err != nil {
        switch e := err.(type) {
        case *NotFoundError:
            fmt.Println("Handle not found:", e.Error())
        case *PermissionError:
            fmt.Println("Handle permission error:", e.Error())
        default:
            fmt.Println("Handle general error:", e.Error())
        }
    }

    err = PerformOperation("restrictedOperation")
    if err != nil {
        switch e := err.(type) {
        case *NotFoundError:
            fmt.Println("Handle not found:", e.Error())
        case *PermissionError:
            fmt.Println("Handle permission error:", e.Error())
        default:
            fmt.Println("Handle general error:", e.Error())
        }
    }
}

在此示例中,我们有两种自定义错误类型:NotFoundErrorPermissionError。当我们在功能中遇到这些错误(FindEntityPerformOperation)时,我们返回这些类型的实例。在我们的主要功能中,我们可以使用类型开关以不同的方式处理不同类型的错误。

这种方法在错误处理方面提供了灵活性和清晰度,并允许呼叫者根据遇到的错误类型做出不同的反应。

4.错误的fmt包:

fmt软件包可用于格式化错误字符串。 errorf函数对此特别方便:

return fmt.Errorf("error occurred while processing %s", itemName)

5.包装错误:

从GO 1.13开始,标准库增加了使用fmt.Errorf%w动词包装错误的支持。这使您可以在保留原始错误的同时将错误包裹在其他上下文中:

if err != nil {
    return fmt.Errorf("failed processing: %w", err)
}

然后,您可以稍后拆开并使用errors.Iserrors.As进行检查。

6.检查错误:

errors软件包提供了用于处理错误的实用程序。

errors.Is:检查错误是错误的错误还是包装版本。
errors.As:检查错误是否匹配某个类型,如果是的,则获取键入值。

var specificErr MyError
if errors.As(err, &specificErr) {
    // handle specificErr
}

最佳实践:

  • 始终明确处理错误。除非您确定这样做是安全的,否则不要忽略它们。
  • 包装错误时,提供上下文,以便可以追踪根本原因。
  • 避免有太多错误返回值。如果功能返回多个错误,请考虑是否有一种简化或分解函数的方法。
  • 明智地使用自定义错误类型。通常,一个简单的错误字符串就足够了。

遵循这些实践并了解GO的独特错误处理方法,您可以编写更多的弹性和可维护代码。