【Go系列】 Go的錯誤處理

承上啟下

? ? ? ? 上一篇文章中介紹了struct和interface,在Go語言中,是沒有Class這個概念的,我們可以通過Struct和方法的組合,來實現Class。我們通過Struct嵌套來實現繼承這樣的一種機制,并且不用設置虛函數這樣的特殊說明。同時,實現接口的方式也是隱式實現,當我們實現了一組接口的所有的方法時,默認一定使用了這組接口了。只能說這樣的方法很不直觀,但是編譯器的靜態代碼分析還是能夠發現的。但是如果是對于程序中的error,Go又會怎么處理呢?

開始學習

錯誤類型(error

在Go語言中,error?是一個內建的接口類型,用于表示錯誤狀態。它是Go錯誤處理的核心部分,提供了一種標準的方式來傳達函數或方法執行失敗的信息。以下是?error?接口的定義:

type error interface {Error() string
}

這個接口非常簡單,只包含一個?Error()?方法,該方法返回一個描述錯誤的字符串。

創建錯誤

創建錯誤通常有以下幾種方式:

errors.New:這是最簡單的方式,用于創建一個包含給定錯誤消息的新錯誤。

err := errors.New("something went wrong")

??????fmt.Errorf:這個函數允許你使用格式化字符串創建錯誤,類似于?fmt.Sprintf

err := fmt.Errorf("invalid argument: %v", value)

自定義錯誤類型:你可以通過實現?error?接口來自定義錯誤類型。

type MyError struct {Msg stringCode int
}func (e *MyError) Error() string {return fmt.Sprintf("error %d: %s", e.Code, e.Msg)
}func doSomething() error {return &MyError{Msg: "operation failed", Code: 42}
}

錯誤處理

在Go中,錯誤處理通常是顯式的,以下是一些常見的錯誤處理模式:

檢查錯誤:在調用可能返回錯誤的函數或方法后,應該立即檢查錯誤。

result, err := doSomething()
if err != nil {// 處理錯誤return err
}
// 使用result

錯誤傳遞:如果當前函數無法處理錯誤,它應該將錯誤返回給調用者。

func doSomething() error {result, err := doAnotherThing()if err != nil {return err // 傳遞錯誤}// 使用resultreturn nil
}

錯誤包裝:有時,你可能想在返回錯誤時添加額外的上下文信息。可以使用?fmt.Errorf?和?%w?標志來包裝錯誤。

func doSomething() error {err := doAnotherThing()if err != nil {return fmt.Errorf("failed to do something: %w", err)}return nil
}

使用?%w?標志包裝的錯誤可以通過?errors.Is?或?errors.As?進行檢查。

錯誤檢查和斷言

  • errors.Is:用于檢查錯誤鏈中是否包含特定錯誤。
if errors.Is(err, targetErr) {// err 是 targetErr 或其包裝
}
  • errors.As:用于將錯誤斷言為特定類型。
var targetErr *MyError
if errors.As(err, &targetErr) {// err 是 *MyError 類型
}

Defer函數?

在Go語言中,defer?語句用于確保在函數返回之前執行特定的函數調用(延遲調用)。這對于資源清理、解鎖、關閉文件描述符等場景非常有用,因為它可以確保這些操作總是被執行,即使在發生錯誤或者提前返回的情況下。

defer?語句的語法

defer?語句的語法非常簡單:

defer 函數調用

這里的“函數調用”可以是任何函數或方法調用,包括內置函數、用戶定義的函數以及方法。

defer?語句的行為

以下是?defer?語句的一些關鍵行為:

  1. 延遲執行defer?語句會在包含它的函數即將返回之前執行。無論函數是正常返回還是由于錯誤返回,defer?語句都會被執行。

  2. 參數評估defer?語句中的參數(如果有的話)是在執行?defer?語句時評估的,而不是在執行延遲函數時。

  3. 執行順序:如果同一個函數中有多個?defer?語句,它們會按照后進先出(LIFO)的順序執行。也就是說,最后一個?defer?的函數調用會最先執行。

下面是一個使用?defer?的示例,展示了如何在文件操作后確保文件被關閉:

func main() {f, err := os.Open("file.txt")if err != nil {log.Fatalf("failed to open file: %s", err)}defer f.Close() // 延遲關閉文件// 讀取文件內容...
}

在這個例子中,無論后續的文件操作是否成功,f.Close()?都會在?main?函數返回之前被調用。

嵌套?defer

你可以在?defer?語句中調用另一個函數,該函數內部也可以有?defer?語句:

func main() {defer func() {// 這里的 defer 會最后執行defer fmt.Println("Deferred inside defer")fmt.Println("Outer defer")}()fmt.Println("Hello")
}

在這個例子中,輸出順序將是:

Hello
Outer defer
Deferred inside defer

這是因為內部的?defer?語句在執行外部的?defer?時被調用,然后才執行外部的?defer?語句。

Recover

在Go語言中,recover?是一個內建函數,它用于捕獲運行時的恐慌(panic)。當程序發生恐慌時,正常的函數執行流程會被立即停止,程序將進入恐慌狀態,執行所有已注冊的延遲函數(deferred functions)。如果在延遲函數中調用了?recover,則可以捕獲當前的恐慌,并且使程序恢復正常執行而不是崩潰。

recover?的語法

recover?的語法非常簡單,它不接受任何參數,并且返回一個?interface{}?類型的值:

recover()

如果?recover?被直接調用(不在延遲函數中),它將不會做任何事情,并且返回?nil

recover?的行為

下面是?recover?的一些關鍵行為:

  1. 只能在延遲函數中生效recover?只能在?defer?語句調用的延遲函數中使用。如果?recover?在其他任何地方被調用,它將不會捕獲任何恐慌。

  2. 返回恐慌值:如果當前的goroutine正處于恐慌狀態,recover?將捕獲到恐慌值,并返回該值,使得程序可以繼續執行正常的流程。

  3. 恢復正常執行:一旦?recover?成功捕獲了恐慌,程序將不會繼續傳播恐慌,而是會繼續執行延遲函數中的剩余代碼,然后返回到恐慌發生點的函數調用者。

下面是一個使用?recover?的示例:

package mainimport "fmt"func main() {defer func() {if r := recover(); r != nil {fmt.Println("Recovered in main:", r)}}()fmt.Println("Start")panic("Something bad happened")fmt.Println("End") // 這行代碼不會被執行
}// 輸出:
// Start
// Recovered in main: Something bad happened

在這個例子中,panic?觸發了一個錯誤,但隨后在延遲函數中通過?recover?被捕獲。因此,程序沒有崩潰,而是打印了恢復信息。

?異常處理對比

??defer?和?recover?是Go語言中用于錯誤處理的機制,而?try-catch?是許多其他面向對象編程語言(如Java、C++、C#等)中用于異常處理的機制。Go語言最為人所詬病的就是滿屏的if err != nil這樣的語法,而在其他的高級語言中大多是一個try{}catch{}finally{}直接捕獲了所有的代碼。這是由于在Go中建議對每個error都單獨做處理,如果非要實現try catch的方法,更多時候是通過defer recover實現的。

defer?和?recover

  • 錯誤處理:在Go中,defer?和?recover?通常用于處理恐慌(panic),這類似于其他語言中的異常。defer?語句用于延遲執行一個函數調用,而?recover?用于捕獲恐慌。

  • 使用場景defer?和?recover?通常用于處理運行時錯誤,這些錯誤是意外的,并且可能導致程序無法繼續執行。

  • 語法

    func someFunction() {defer func() {if r := recover(); r != nil {// 處理恐慌}}()// 可能發生恐慌的代碼
    }
    
  • 顯式性defer?和?recover?需要顯式地聲明,它們不會自動捕獲錯誤。

  • 性能defer?和?recover?的性能開銷相對較低,因為它們只在發生恐慌時才執行。

try-catch

  • 異常處理try-catch?用于捕獲和處理異常。異常通常表示程序運行時的錯誤或異常情況。

  • 使用場景try-catch?用于處理各種錯誤,包括運行時錯誤和可預見的錯誤情況。

  • 語法(以Java為例):

    try {// 可能拋出異常的代碼
    } catch (SomeException e) {// 處理異常
    }
    
  • 自動捕獲try-catch?塊自動捕獲在?try?塊中拋出的異常,無需顯式聲明。

  • 性能try-catch?可能會有更高的性能開銷,因為異常的拋出和捕獲涉及堆棧跟蹤的創建。

對比

  • 錯誤/異常類型:Go使用?error?類型來表示可預見的錯誤,而?panic?和?recover?用于處理意外情況。try-catch?通常用于處理所有類型的錯誤和異常。

  • 傳播機制:在Go中,錯誤必須顯式地返回和檢查。恐慌會自動傳播,直到被?recover?捕獲。在?try-catch?機制中,異常會自動向上傳播,直到遇到匹配的?catch?塊。

  • 資源管理:Go使用?defer?來確保資源被正確釋放,而其他語言可能使用?finally?塊或RAII(Resource Acquisition Is Initialization)。

  • 控制流try-catch?允許異常改變程序的控制流,而Go的?defer?和?recover?主要用于恢復程序狀態,而不是改變控制流。

  • 代碼風格:Go鼓勵通過返回錯誤值來進行錯誤處理,這有助于保持代碼的清晰和簡潔。try-catch?可能導致代碼中異常處理邏輯的分散。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44941.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44941.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44941.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何防止第三方DLL注入自己的進程?

PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY 結構 struct _PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY {union {DWORD Flags;struct {DWORD MicrosoftSignedOnly : 1;DWORD StoreSignedOnly : 1;DWORD MitigationOptIn : 1;DWORD AuditMicrosoftSignedOnly : 1;DWORD Audit…

C語言 ——— 實用調試技巧(Visual Studio)

目錄 Debug 和 Release 的區別 F10 --- 逐過程調試 & F11 --- 逐語句調試 F9 --- 新建/切換斷點 & F5 --- 開始調試 shift F5 & ctrl F5 Debug 和 Release 的區別 Debug:通常為調試版本,它包含調試信息,并且不作任何優化…

一 GD32 MCU 開發環境搭建

GD32 系列為通用型 MCU ,所以開發環境也可以使用通用型的 IDE ,目前使用較多的是 KEIL、 IAR 、 GCC 和 Embedded Builder ,客戶可以根據個人喜好來選擇相應的開發環境。 目錄 1、使用 Keil 開發 GD32 目前市面通用的MDK for ARM版本有Kei…

華為OD機試真題2024版-路口最短時間問題

題目描述 假定街道是棋盤型的,每格距離相等,車輛通過每格街道需要時間均為 timePerRoad; 街道的街口(交叉點)有交通燈,燈的周期 T(lights[row][col]) 各不相同; 車輛可直行、左轉和右轉&…

企業網三層架構

企業網三層架構:是一種層次化模型設計,旨在將復雜的網絡設計分成三個層次,每個層次都著重于某些特定的功能,以提高效率和穩定性。 企業網三層架構層次: 接入層:使終端設備接入到網絡中來,提供…

Python爬蟲教程第二篇:進階技巧與實戰案例

Python爬蟲教程第二篇:進階技巧與實戰案例 在上一篇教程中,我們學習了Python爬蟲的基礎概念、基本流程以及一個簡單的入門實踐案例。本篇教程將帶領大家進一步探索Python爬蟲的進階技巧,并提供一個實戰案例,幫助大家提升爬蟲技能…

Android12 MultiMedia框架之GenericSource extractor

前面兩節學習到了各種Source的創建和extractor service的啟動,本節將以本地播放為例記錄下GenericSource是如何創建一個extractor的。extractor是在PrepareAsync()方法中被創建出來的,為了不過多贅述,我們直接從GenericSource的onPrepareAsyn…

Mojolicious命令行工具:自動化Web開發的瑞士軍刀

Mojolicious是一個高性能的、基于Perl的Web開發框架,它提供了一整套工具來簡化Web開發流程。其中,Mojolicious的命令行工具集是其強大功能的一部分,允許開發者快速生成項目模板、運行開發服務器、執行各種開發任務等。本文將詳細介紹Mojolici…

qt 自定義信號號槽 簡單舉例

在Qt中,自定義信號和槽是一種非常靈活的方式來處理對象之間的通信。以下是一個簡單的例子,展示了如何定義和使用自定義的信號和槽。 首先,我們定義一個名為MyClass的類,該類繼承自QObject,并聲明一個自定義信號和一個…

13_Shell系統函數

13_Shell系統函數和自定義函數 一、系統函數 basename 獲取文件名 #!/bin/bash#basename 相對路徑文件名 basename ./1.sh#basename 絕對路徑文件名 basename /tmp/1.sh#basename 去除文件后綴名 basename /tmp/1.sh .shdirname 獲取文件所在目錄名 #!/bin/bash#dirname 相對路…

Redis持久化RDB,AOF

目 錄 CONFIG動態修改配置 慢查詢 持久化 在上一篇主要對redis的了解入門,安裝,以及基礎配置,多實例的實現:redis的安裝看我上一篇: Redis安裝部署與使用,多實例 redis是擋在MySQL前面的,運行在內存…

Week 6-楊帆-學習總結

- 46 語義分割和數據集 語義分割概念 語義分割是一種計算機視覺任務,其目標是將圖像分割成屬于不同語義類別的區域。與目標檢測不同,語義分割關注的是像素級別的標注和預測,能夠識別并理解圖像中每一個像素的內容。這使得語義分割在理解圖像…

產品經理-研發流程-敏捷開發-迭代-需求評審及產品規劃(15)

敏捷開發是以用戶的需求進化為核心,采用迭代、循序漸進的方法進行軟件開發。 通俗來說,敏捷開發是一個軟件開發流程,是一個采用了迭代方法的開發流程 簡單來說,迭代就是把一個大產品拆分出一些最小的實現單位。完成不同的迭代就最…

機器學習筑基篇,Jupyter Notebook 精簡指南

[ 知識是人生的燈塔,只有不斷學習,才能照亮前行的道路 ] 0x00 Jupyter Notebook 簡明指南 描述:前面我們已經在機器學習工作站(Ubuntu 24.04 Desktop Geforce RTX 4070Ti SUPER)中安裝 Anaconda 工具包,其…

老物件線上3D回憶展拓寬了藝術作品的展示空間和時間-深圳華銳視點

在數字技術的浪潮下,3D線上畫展為藝術家們開啟了一個全新的展示與銷售平臺。這一創新形式不僅拓寬了藝術作品的展示空間,還為廣大觀眾帶來了前所未有的觀賞體驗。 3D線上畫展制作以其獨特的互動性,讓藝術不再是單一的視覺享受。在這里&#x…

數據處理-Matplotlib 繪圖展示

文章目錄 1. Matplotlib 簡介2. 安裝3. Matplotlib Pyplot4. 繪制圖表1. 折線圖2. 散點圖3. 柱狀圖4. 餅圖5. 直方圖 5. 中文顯示 1. Matplotlib 簡介 Matplotlib 是 Python 的繪圖庫,它能讓使用者很輕松地將數據圖形化,并且提供多樣化的輸出格式。 Ma…

如何定義版本號--語義化版本

前言 版本號(version number)是版本的標識號。每一個操作系統(或廣義的講,每一個軟件)都有一個版本號。版本號能使用戶了解所使用的操作系統是否為最新的版本以及它所提供的功能與設施。 例如在Python項目依賴中會看到 requires-python &q…

zdppy+onlyoffice實現重命名文件的功能

參考文檔:https://api.onlyoffice.com/zh/editors/rename 步驟圖: 實現步驟: 用戶在 文檔編輯器中為文檔指定一個新名稱。 文檔編輯器 將文檔的新名稱通知給 文檔管理器。 文檔管理器 將文檔的新名稱發送到 文檔存儲服務,在這里…

使用jsencrypt在web前端對字符串進行Ras加密

話不多說&#xff0c;上代碼 實例代碼 下面方法&#xff0c;在網頁中先引入jsencrypt.min.js。然后調用ToEncrypt方法示例輸出加密&#xff0c;解密后的結果。 <script src"/js/jsencrypt.min.js"></script> //加密測試function ToEncrypt(){// 假設…

synchronized關鍵字詳解

文章目錄 synchronized使用示例實現原理鎖的升級synchronized與可見性synchronized與原子性synchronized與有序性 synchronized synchronized是Java提供的關鍵字譯為同步&#xff0c;是Java中用于實現線程同步的一種機制。它可以確保在同一時間只有一個線程能夠執行某段代碼&a…