天天看點

golang處理錯誤_處理Go中的錯誤

golang處理錯誤

Robust code needs to react correctly to unexpected circumstances like bad user input, faulty network connections, and failing disks. Error handling is the process of identifying when your program is in an unexpected state, and taking steps to record diagnostic information for later debugging.

健壯的代碼需要對意外的情況(例如,錯誤的使用者輸入,錯誤的網絡連接配接和磁盤故障)做出正确的React。 錯誤處理是識别程式何時處于意外狀态并采取步驟記錄診斷資訊以供以後調試的過程。

Unlike other languages that require developers to handle errors with specialized syntax, errors in Go are values with the type

error

returned from functions like any other value. To handle errors in Go, we must examine these errors that functions could return, decide if an error has occurred, and take proper action to protect data and tell users or operators that the error occurred.

與其他要求開發人員使用特殊文法處理錯誤的語言不同,Go中的錯誤是具有從函數傳回的類型

error

值,就像其他值一樣。 要處理Go中的錯誤,我們必須檢查函數可以傳回的這些錯誤,确定是否發生了錯誤,并采取适當的措施保護資料并告知使用者或操作員該錯誤已發生。

産生錯誤 (Creating Errors)

Before we can handle errors, we need to create some first. The standard library provides two built-in functions to create errors:

errors.New

and

fmt.Errorf

. Both of these functions allow you to specify a custom error message that you can later present to your users.

在處理錯誤之前,我們需要先建立一些錯誤。 标準庫提供了兩個内置函數來建立錯誤:

errors.New

fmt.Errorf

。 這兩個功能都允許您指定自定義錯誤消息,以後可以将其呈現給使用者。

errors.New

takes a single argument—an error message as a string that you can customize to alert your users what went wrong.

errors.New

接受一個參數-錯誤消息作為字元串,您可以自定義該消息以警告使用者出了什麼問題。

Try running the following example to see an error created by

errors.New

printed to standard output:

嘗試運作以下示例以檢視錯誤所

errors.New

的錯誤。将

errors.New

列印到标準輸出中:

package main

import (
    "errors"
    "fmt"
)

func main() {
    err := errors.New("barnacles")
    fmt.Println("Sammy says:", err)
}
           
Output
   Sammy says: barnacles
           

We used the

errors.New

function from the standard library to create a new error message with the string

"barnacles"

as the error message. We’ve followed convention here by using lowercase for the error message as the Go Programming Language Style Guide suggests.

我們使用标準庫中的

errors.New

函數來建立新的錯誤消息,并以字元串

"barnacles"

作為錯誤消息。 正如Go程式設計語言樣式指南所建議的那樣,我們在此處遵循約定,對錯誤消息使用小寫字母。

Finally, we used the

fmt.Println

function to combine our error message with

"Sammy says:"

.

最後,我們使用

fmt.Println

函數将錯誤消息與

"Sammy says:"

結合在一起。

The

fmt.Errorf

function allows you to dynamically build an error message. Its first argument is a string containing your error message with placeholder values such as

%s

for a string and

%d

for an integer.

fmt.Errorf

interpolates the arguments that follow this formatting string into those placeholders in order:

使用

fmt.Errorf

函數可以動态生成錯誤消息。 它的第一個參數是一個字元串,其中包含錯誤消息,并帶有占位符值,例如

%s

表示字元串,

%d

表示整數。

fmt.Errorf

将該格式字元串

fmt.Errorf

的參數按順序插入到這些占位符中:

package main

import (
    "fmt"
    "time"
)

func main() {
    err := fmt.Errorf("error occurred at: %v", time.Now())
    fmt.Println("An error happened:", err)
}
           
Output
   An error happened: Error occurred at: 2019-07-11 16:52:42.532621 -0400 EDT m=+0.000137103
           

We used the

fmt.Errorf

function to build an error message that would include the current time. The formatting string we provided to

fmt.Errorf

contains the

%v

formatting directive that tells

fmt.Errorf

to use the default formatting for the first argument provided after the formatting string. That argument will be the current time, provided by the

time.Now

function from the standard library. Similarly to the earlier example, we combine our error message with a short prefix and print the result to standard output using the

fmt.Println

function.

我們使用了

fmt.Errorf

函數來生成一條包含目前時間的錯誤消息。 我們提供給

fmt.Errorf

的格式字元串包含

%v

格式指令,該指令訓示

fmt.Errorf

對格式字元串之後提供的第一個參數使用預設格式。 該參數将是标準庫中的

time.Now

函數提供的目前時間。 與前面的示例類似,我們将錯誤消息與短字首組合在一起,并使用

fmt.Println

函數将結果列印到标準輸出中。

處理錯誤 (Handling Errors)

Typically you wouldn’t see an error created like this to be used immediately for no other purpose, as in the previous example. In practice, it’s far more common to create an error and return it from a function when something goes wrong. Callers of that function will then use an

if

statement to see if the error was present or

nil

—an uninitialized value.

通常,您不會看到像這樣建立的錯誤可以立即用于其他目的,就像前面的示例一樣。 在實踐中,更常見的是建立錯誤并在出現問題時從函數傳回錯誤。 然後,該函數的調用者将使用

if

語句檢視錯誤是否存在或為

nil

未初始化的值)。

This next example includes a function that always returns an error. Notice when you run the program that it produces the same output as the previous example even though a function is returning the error this time. Declaring an error in a different location does not change the error’s message.

下一個示例包括一個始終傳回錯誤的函數。 請注意,當您運作該程式時,即使該函數這次傳回錯誤,它也會産生與上一個示例相同的輸出。 在其他位置聲明錯誤不會更改錯誤消息。

package main

import (
    "errors"
    "fmt"
)

func boom() error {
    return errors.New("barnacles")
}

func main() {
    err := boom()

    if err != nil {
        fmt.Println("An error occurred:", err)
        return
    }
    fmt.Println("Anchors away!")
}
           
Output
   An error occurred: barnacles
           

Here we define a function called

boom()

that returns a single

error

that we construct using

errors.New

. We then call this function and capture the error with the line

err := boom()

. Once we assign this error, we check to see if it was present with the

if err != nil

conditional. Here the conditional will always evaluate to

true

, since we are always returning an

error

from

boom()

.

在這裡,我們定義了一個名為

boom()

的函數,該函數傳回一個使用

errors.New

構造的

error

。 然後,我們調用此函數并使用

err := boom()

行捕獲錯誤。 配置設定了該錯誤後,我們将檢查條件

if err != nil

存在

if err != nil

。 因為我們總是從

boom()

傳回

error

,是以條件将始終為

true

This won’t always be the case, so it’s good practice to have logic handling cases where an error is not present (

nil

) and cases where the error is present. When the error is present, we use

fmt.Println

to print our error along with a prefix as we have done in earlier examples. Finally, we use a

return

statement to skip the execution of

fmt.Println("Anchors away!")

, since that should only execute when no error occurred.

情況并非總是如此,是以最好的做法是進行邏輯處理,以解決不存在錯誤的情況(

nil

)和存在錯誤的情況。 當錯誤出現時,我們使用

fmt.Println

來列印錯誤以及帶有字首的

fmt.Println

,就像在前面的示例中所做的那樣。 最後,我們使用

return

語句跳過

fmt.Println("Anchors away!")

執行,因為僅在沒有錯誤發生時才執行。

Note: The

if err != nil

construction shown in the last example is the workhorse of error handling in the Go programming language. Wherever a function could produce an error, it’s important to use an

if

statement to check whether one occurred. In this way, idiomatic Go code naturally has its “happy path” logic at the first indent level, and all the “sad path” logic at the second indent level.

注意:最後一個示例中顯示的

if err != nil

構造是Go程式設計語言中錯誤處理的主力軍。 無論函數在何處産生錯誤,使用

if

語句檢查是否發生錯誤都是很重要的。 這樣,慣用的Go代碼自然在第一個縮進級别具有其“快樂路徑”邏輯,而在第二個縮進級别具有所有“悲傷的路徑”邏輯。

If statements have an optional assignment clause that can be used to help condense calling a function and handling its errors.

If語句具有可選的指派子句,可用于幫助壓縮壓縮調用函數和處理其錯誤。

Run the next program to see the same output as our earlier example, but this time using a compound

if

statement to reduce some boilerplate:

運作下一個程式以檢視與前面的示例相同的輸出,但是這次使用複合

if

語句來減少一些樣闆:

package main

import (
    "errors"
    "fmt"
)

func boom() error {
    return errors.New("barnacles")
}

func main() {
    if err := boom(); err != nil {
        fmt.Println("An error occurred:", err)
        return
    }
    fmt.Println("Anchors away!")
}
           
Output
   An error occurred: barnacles
           

As before, we have a function,

boom()

, that always returns an error. We assign the error returned from

boom()

to

err

as the first part of the

if

statement. In the second part of the

if

statement, following the semicolon, that

err

variable is then available. We check to see if the error was present and print our error with a short prefix string as we’ve done previously.

和以前一樣,我們有一個函數

boom()

,該函數始終傳回錯誤。 我們将從

boom()

傳回的錯誤配置設定給

err

作為

if

語句的第一部分。 在

if

語句的第二部分中,在分号之後,然後是

err

變量。 我們檢查錯誤是否存在,并像以前一樣用短字首字元串列印錯誤。

In this section, we learned how to handle functions that only return an error. These functions are common, but it’s also important to be able to handle errors from functions that can return multiple values.

在本節中,我們學習了如何處理僅傳回錯誤的函數。 這些函數很常見,但是處理能夠傳回多個值的函數中的錯誤也很重要。

與值一起傳回錯誤 (Returning Errors Alongside Values)

Functions that return a single error value are often those that effect some stateful change, like inserting rows to a database. It’s also common to write functions that return a value if they completed successfully along with a potential error if that function failed. Go permits functions to return more than one result, which can be used to simultaneously return a value and an error type.

傳回單個錯誤值的函數通常是那些會引起狀态變化的函數,例如将行插入資料庫。 如果傳回的值成功完成,則傳回一個值;如果該函數失敗,則可能傳回錯誤。 Go允許函數傳回多個結果,這些結果可用于同時傳回一個值和一個錯誤類型。

To create a function that returns more than one value, we list the types of each returned value inside parentheses in the signature for the function. For example, a

capitalize

function that returns a

string

and an

error

would be declared using

func capitalize(name string) (string, error) {}

. The

(string, error)

part tells the Go compiler that this function will return a

string

and an

error

, in that order.

要建立一個傳回多個值的函數,我們在函數簽名的括号内列出每個傳回值的類型。 例如,使用

func capitalize(name string) (string, error) {}

聲明傳回

string

error

capitalize

函數。

(string, error)

部分告訴Go編譯器此函數将按此順序傳回一個

string

和一個

error

Run the following program to see the output from a function that returns both a

string

and an

error

:

運作以下程式以檢視傳回

string

error

的函數的輸出:

package main

import (
    "errors"
    "fmt"
    "strings"
)

func capitalize(name string) (string, error) {
    if name == "" {
        return "", errors.New("no name provided")
    }
    return strings.ToTitle(name), nil
}

func main() {
    name, err := capitalize("sammy")
    if err != nil {
        fmt.Println("Could not capitalize:", err)
        return
    }

    fmt.Println("Capitalized name:", name)
}
           
Output
   Capitalized name: SAMMY
           

We define

capitalize()

as a function that takes a string (the name to be capitalized) and returns a string and an error value. In

main()

, we call

capitalize()

and assign the two values returned from the function to the

name

and

err

variables by separating them with commas on the left-hand side of the

:=

operator. After this, we perform our

if err != nil

check as in earlier examples, printing the error to standard output using

fmt.Println

if the error was present. If no error was present, we print

Capitalized name: SAMMY

.

我們将

capitalize()

定義為一個接受字元串(要大寫的名稱)并傳回字元串和錯誤值的函數。 在

main()

,我們調用

capitalize()

,并将函數傳回的兩個值配置設定給

name

err

變量,方法是在

:=

運算符的左側用逗号分隔。 此後,我們像前面的示例一樣執行

if err != nil

檢查,如果存在錯誤,則使用

fmt.Println

将錯誤列印到标準輸出。 如果沒有錯誤,則列印

Capitalized name: SAMMY

Try changing the string

"sammy"

in

name, err := capitalize("sammy")

to the empty string

("")

and you’ll receive the error

Could not capitalize: no name provided

instead.

嘗試改變字元串

"sammy"

name, err := capitalize("sammy")

為空字元串

("")

您會收到錯誤

Could not capitalize: no name provided

來代替。

The

capitalize

function will return an error when callers of the function provide an empty string for the

name

parameter. When the

name

parameter is not the empty string,

capitalize()

uses

strings.ToTitle

to capitalize the

name

parameter and returns

nil

for the error value.

當函數的調用者為

name

參數提供空字元串時,

capitalize

函數将傳回錯誤。 當

name

參數不是空字元串時,

capitalize()

使用

strings.ToTitle

name

參數大寫,并為錯誤值傳回

nil

There are some subtle conventions that this example follows that is typical of Go code, yet not enforced by the Go compiler. When a function returns multiple values, including an error, convention requests that we return the

error

as the last item. When returning an

error

from a function with multiple return values, idiomatic Go code also will set each non-error value to a zero value. Zero values are, for example, an empty string for strings,

for integers, an empty struct for struct types, and

nil

for interface and pointer types, to name a few. We cover zero values in more detail in our tutorial on variables and constants.

此示例遵循一些微妙的約定,這些約定是Go代碼的典型,但Go編譯器未強制執行。 當一個函數傳回多個值(包括錯誤)時,約定要求我們将

error

作為最後一項傳回。 從具有多個傳回值的函數傳回

error

,慣用的Go代碼還将把每個非錯誤值設定為零值 。 例如,零值是用于字元串的空字元串,用于整數的

,用于結構類型的空struct和用于接口和指針類型的

nil

(僅舉幾例)。 在有關變量和常量的教程中,我們将更詳細地介紹零值。

減少樣闆 (Reducing boilerplate)

Adhering to these conventions can become tedious in situations where there are many values to return from a function. We can use an anonymous function to help reduce the boilerplate. Anonymous functions are procedures assigned to variables. In contrast to the functions we have defined in earlier examples, they are only available within the functions where you declare them—this makes them perfect to act as short pieces of reusable helper logic.

在從函數傳回許多值的情況下,遵守這些約定可能會變得乏味。 我們可以使用匿名函數來幫助減少樣闆。 匿名函數是配置設定給變量的過程。 與我們在前面的示例中定義的函數相反,它們僅在聲明它們的函數中可用-這使它們非常适合用作可重用的輔助邏輯的簡短片段。

The following program modifies the last example to include the length of the name that we’re capitalizing. Since it has three values to return, handling errors could become cumbersome without an anonymous function to assist us:

以下程式修改了最後一個示例,以包含我們要大寫的名稱的長度。 由于要傳回三個值,是以如果沒有匿名函數來協助我們,處理錯誤就會變得很麻煩:

package main

import (
    "errors"
    "fmt"
    "strings"
)

func capitalize(name string) (string, int, error) {
    handle := func(err error) (string, int, error) {
        return "", 0, err
    }

    if name == "" {
        return handle(errors.New("no name provided"))
    }

    return strings.ToTitle(name), len(name), nil
}

func main() {
    name, size, err := capitalize("sammy")
    if err != nil {
        fmt.Println("An error occurred:", err)
    }

    fmt.Printf("Capitalized name: %s, length: %d", name, size)
}
           
Output
   Capitalized name: SAMMY, length: 5
           

Within

main()

, we now capture the three returned arguments from

capitalize

as

name

,

size

, and

err

, respectively. We then check to see if

capitalize

returned an

error

by checking if the

err

variable was not equal to

nil

. This is important to do before attempting to use any of the other values returned by

capitalize

, because the anonymous function,

handle

, could set those to zero values. Since no error occurred because we provided the string

"sammy"

, we print out the capitalized name and its length.

現在在

main()

,我們分别從

capitalize

捕獲的三個傳回參數分别為

name

size

err

。 然後,我們通過檢查

err

變量是否不等于

nil

來檢查

capitalize

傳回了

error

。 在嘗試使用由

capitalize

傳回的任何其他值之前,這樣做很重要,因為匿名函數

handle

可以将這些值設定為零。 由于沒有錯誤,因為我們提供了字元串

"sammy"

,是以我們列印出大寫的名稱及其長度。

Once again, you can try changing

"sammy"

to the empty string

("")

to see the error case printed (

An error occurred: no name provided

).

再次,您可以嘗試将

"sammy"

更改為空字元串

("")

以檢視列印的錯誤情況(

An error occurred: no name provided

)。

Within

capitalize

, we define the

handle

variable as an anonymous function. It takes a single error and returns identical values in the same order as the return values of

capitalize

.

handle

sets those values to zero values and forwards the

error

passed as its argument as the final return value. Using this, we can then return any errors encountered in

capitalize

by using the

return

statement in front of the call to

handle

with the

error

as its parameter.

capitalize

,我們将

handle

變量定義為匿名函數。 它隻有一個錯誤,并且以與

capitalize

的傳回值相同的順序傳回相同的值。

handle

将這些值設定為零值,并将作為其參數傳遞的

error

轉發為最終傳回值。 然後,通過使用調用前面的

return

語句以

error

作為參數進行

handle

,我們可以傳回

capitalize

遇到的任何錯誤。

Remember that

capitalize

must return three values all the time, since that’s how we defined the function. Sometimes we don’t want to deal with all the values that a function could return. Fortunately, we have some flexibility in how we can use these values on the assignment side.

請記住,

capitalize

必須始終傳回三個值,因為這就是我們定義函數的方式。 有時我們不想處理函數可能傳回的所有值。 幸運的是,我們在配置設定方面可以靈活地使用這些值。

處理多次傳回功能中的錯誤 (Handling Errors from Multi-Return Functions)

When a function returns many values, Go requires us to assign each to a variable. In the last example, we do this by providing names for the two values returned from the

capitalize

function. These names should be separated by commas and appear on the left-hand side of the

:=

operator. The first value returned from

capitalize

will be assigned to the

name

variable, and the second value (the

error

) will be assigned to the variable

err

. Occasionally, we’re only interested in the error value. You can discard any unwanted values that functions return using the special

_

variable name.

當一個函數傳回許多值時,Go要求我們将每個值配置設定給一個變量。 在最後一個示例中,我們通過提供從

capitalize

函數傳回的兩個值的名稱來實作此目的。 這些名稱應以逗号分隔,并出現在

:=

運算符的左側。 從

capitalize

傳回的第一個值将配置設定給

name

變量,第二個值(

error

)将配置設定給變量

err

。 有時,我們隻對錯誤值感興趣。 您可以使用特殊的

_

變量名來丢棄函數傳回的所有不需要的值。

In the following program, we’ve modified our first example involving the

capitalize

function to produce an error by passing in the empty string

("")

. Try running this program to see how we’re able to examine just the error by discarding the first returned value with the

_

variable:

在下面的程式中,我們修改了涉及

capitalize

函數的第一個示例,以通過傳入空字元串

("")

産生錯誤。 嘗試運作該程式,看看如何通過使用

_

變量丢棄第一個傳回的值來僅檢查錯誤:

package main

import (
    "errors"
    "fmt"
    "strings"
)

func capitalize(name string) (string, error) {
    if name == "" {
        return "", errors.New("no name provided")
    }
    return strings.ToTitle(name), nil
}

func main() {
    _, err := capitalize("")
    if err != nil {
        fmt.Println("Could not capitalize:", err)
        return
    }
    fmt.Println("Success!")
}
           
Output
   Could not capitalize: no name provided
           

Within the

main()

function this time, we assign the capitalized name (the

string

returned first) to the underscore variable (

_

). At the same time, we assign the

error

returned by

capitalize

to the

err

variable. We then check if the error was present in the

if err != nil

conditional. Since we have hard-coded an empty string as an argument to

capitalize

in the line

_, err := capitalize("")

, this conditional will always evaluate to

true

. This produces the output

"Could not capitalize: no name provided"

printed by the call to the

fmt.Println

function within the body of the

if

statement. The

return

after this will skip the

fmt.Println("Success!")

.

這次在

main()

函數中,我們将大寫名稱(首先傳回的

string

)配置設定給下劃線變量(

_

)。 同時,我們将

capitalize

傳回的

error

配置設定給

err

變量。 然後,我們檢查if

if err != nil

存在

if err != nil

。 因為我們有寫死一個空字元串作為參數,以

capitalize

該行

_, err := capitalize("")

這将有條件始終評估為

true

。 這将生成對

if

語句主體中的

fmt.Println

函數的調用列印的輸出

"Could not capitalize: no name provided"

。 此後的

return

将跳過

fmt.Println("Success!")

結論 (Conclusion)

We’ve seen many ways to create errors using the standard library and how to build functions that return errors in an idiomatic way. In this tutorial, we’ve managed to successfully create various errors using the standard library

errors.New

and

fmt.Errorf

functions. In future tutorials, we’ll look at how to create our own custom error types to convey richer information to users.

我們已經看到了許多使用标準庫建立錯誤的方法,以及如何建構以慣用方式傳回錯誤的函數。 在本教程中,我們設法使用标準庫

errors.New

fmt.Errorf

函數成功建立了各種錯誤。 在以後的教程中,我們将研究如何建立自己的自定義錯誤類型以向使用者傳達更豐富的資訊。

翻譯自: https://www.digitalocean.com/community/tutorials/handling-errors-in-go

golang處理錯誤

繼續閱讀