<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] # 4.1 問題的演化 錯誤`error`在 Go 中表現為一個內建的接口類型,任何實現了`Error() string`方法的類型都能作為`error`類型進行傳遞,成為錯誤值: ``` type error interface { Error() string } ``` 作為內建接口類型,編譯器負責在參數傳遞檢查時,對值類型所實現的方法進行檢查。 當類型實現了`Error() string`方法后,才允許其作為 error 進行傳遞: ``` // go/src/cmd/compile/internal/gc/universe.go func makeErrorInterface() *types.Type { field := types.NewField() field.Type = types.Types[TSTRING] f := functypefield(fakeRecvField(), nil, []*types.Field{field}) // 查找是否實現了 Error field = types.NewField() field.Sym = lookup("Error") field.Type = f t := types.New(TINTER) t.SetInterface([]*types.Field{field}) return t } ``` ## 4.1.1 錯誤的歷史形態 早期的 Go 甚至沒有錯誤處理 \[Gerrand, 2010\] \[Cox, 2019b\], 當時的`os.Read`函數進行系統調用可能產生錯誤,而該接口是通過`int64`類型進行錯誤返回的: ``` export func Read(fd int64, b *[]byte) (ret int64, errno int64) { r, e := syscall.read(fd, &amp;b[0], int64(len(b))); return r, e } ``` 隨后,Go 團隊將這一`errno`轉換抽象成了一個類型: ``` export type Error struct { s string } func (e *Error) Print() { ... } func (e *Error) String() string { ... } export func Read(fd int64, b *[]byte) (ret int64, err *Error) { r, e := syscall.read(fd, &amp;b[0], int64(len(b))); return r, ErrnoToError(e) } ``` 之后才演變為了 Go 1 中被人們熟知的`error`接口類型。 可見之所以從理解上我們可以將 error 認為是一個接口,是因為在編譯器實現中, 是通過查詢某個類型是否實現了`Error`方法來創建 Error 類型的。 ## 4.1.2 處理錯誤的基本策略 由于 Go 中的錯誤處理設計得非常簡潔,在其他現代編程語言里都幾乎找不見此類做法。 Go 團隊也曾多次撰寫文章來教導 Go 語言的用戶 \[Gerrand, 2011\] \[Pike, 2015\]。 無論怎樣,非常常見的策略包含哨兵錯誤、自定義錯誤以及隱式錯誤三種。 ### 哨兵錯誤 哨兵錯誤的處理方式通過特定值表示成功和不同錯誤,依靠調用方對錯誤進行檢查: ``` if err === ErrSomething { ... } ``` 例如,比較著名的`io.EOF = errors.New("EOF")`。 這種錯誤處理的方式引入了上下層代碼的依賴,如果被調用方的錯誤類型發生了變化, 則調用方也需要對代碼進行修改: ``` func readf(path string) error { err := file.Open(path) if err != nil { return fmt.Errorf("cannot open file: %v", err) } } func main() { err := readf("~/.ssh/id_rsa.pub") if strings.Contains(err.Error(), "not found") { ... } } ``` 這類錯誤處理的方式是非常危險的,因為它在調用方和被調用方之間建立了牢不可破的依賴關系。 除此之外,哨兵錯誤還有一個相當致命的危險,那就是這種方式所定義的錯誤并非常量,例如: ``` package io var EOF = errors.New("EOF") ``` 而當我們將此錯誤類型公開給其他包使用后,我們非常難以避免這種事情發生: ``` package main import "io" func init() { io.EOF = nil } ``` 這種事情甚至嚴重到,如果在引入的依賴中,有人惡意將這樣驗證錯誤值進行修改的代碼包含進去, 將導致重大的安全問題: ``` import "cropto/rsa" func init() { rsa.ErrVerification = nil } ``` 在碩大的代碼依賴中,我們幾乎無法保證這種惡意代碼不會出現在某個依賴的包中。 為了安全起見,變量錯誤類型可以修改為常量錯誤: ``` -var EOF = errors.New("EOF") +const EOF = ioError("EOF") +type ioEorror string + +func (e ioError) Error() string { return string(e) } ``` ### 自定義錯誤 ``` if err, ok := err.(SomeErrorType); ok { ... } ``` 這類錯誤處理的方式通過自定義的錯誤類型來表示特定的錯誤,同樣依賴上層代碼對錯誤值進行檢查, 不同的是需要使用類型斷言進行檢查。 例如: ``` type CustomizedError struct { Line int Msg string File string } func (e CustomizedError) Error() string { return fmt.Sprintf("%s:%d: %s", e.File, e.Line, e.Msg) } ``` 這種錯誤處理的好處在于,可以將錯誤包裝起來,提供更多的上下文信息, 但錯誤的實現方必須向上層公開實現的錯誤類型,不可避免的同樣需要產生依賴關系。 ### 隱式錯誤 ``` if err != nil { return err } ``` 這種錯誤處理的方式直接返回錯誤的任何細節,直接將錯誤進一步報告給上層。這種情況下, 錯誤在當前調用方這里完全沒有進行任何加工,與沒有進行處理幾乎是等價的, 這會產生的一個致命問題在于:丟失調用的上下文信息,如果某個錯誤連續向上層傳播了多次, 那么上層代碼可能在輸出某個錯誤時,根本無法判斷該錯誤的錯誤信息究竟從哪兒傳播而來。 以上面提到的文件打開的例子為例,錯誤信息可能就只有一個`not found`。 ## 4.1.3 處理錯誤的本質 回顧處理錯誤的基本策略我們可以看出,在 Go 語言中錯誤處理這一話題基本上是圍繞以下三個問題進行的: 1. 錯誤值檢查:如何對一個傳播鏈條中的錯誤類型進行斷言? 2. 錯誤格式與上下文:出現錯誤時,沒有足夠的堆棧信息,如何增強錯誤發生時的上下文信息并合理格式化一個錯誤? 3. 錯誤處理語義:每個返回錯誤的函數都要求調用方進行顯式處理,處理方式啰嗦而冗長,如何減少這種代碼出現的密集程度? 我們在后面的小節中對這些問題進行一一討論。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看