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處理錯誤