)
錯誤處理Go沒有try / catch。日常約定是能預期的失敗參數不對、查無此人、除數為 0→ 返回error由調用方決定怎么處理。不該發生、程序狀態已不可信下標越界、斷言失敗、嚴重邏輯崩壞→ 用panic必要時再在上層用deferrecover兜底。本文用「九九乘法表」同一個業務場景對比這兩種寫法的差別。業務錯誤 vs 程序錯誤類型常見手段出錯后同函數里后面的代碼典型場景業務錯誤返回(結果, error)還能繼續走只要你沒return用戶輸入非法、業務校驗失敗程序錯誤panic不再執行即使recover了也是如此嚴重異常、不可恢復狀態記住口訣業務錯誤是「報告給調用方」程序錯誤是「當前這條執行路徑斷了」。業務錯誤返回errorerror是內置接口只有一個方法Error() string。沒出錯返回nil有錯返回非nil。下面要求參數必須是9才打印九九表傳8時返回錯誤。注意處理完錯誤之后main里的「其余邏輯」仍然會執行。packagemainimport(errorsfmt)// 業務校驗失敗返回 error不中斷整個程序funcnn(aint)(bool,error){ifa!9{returnfalse,errors.New(請輸入9)}fori:1;ia;i{fork:1;ki;k{fmt.Printf(%d * %d %d ,k,i,i*k)}fmt.Println()}returntrue,nil}funcmain(){success,err:nn(8)iferr!nil{fmt.Println(err,執行失敗)}else{fmt.Println(success,執行成功)}// 業務錯誤只是「這次調用失敗」后面的代碼照常走fmt.Println(其余邏輯)}輸出類似請輸入9 執行失敗 其余邏輯要點用errors.New(說明)或fmt.Errorf(a%d 非法, a)創建錯誤。調用方標準姿勢if err ! nil { ... }。返回錯誤后函數正常結束調用方可以打日志、換參數重試也可以繼續干別的事。程序錯誤panic把「請輸入 9」改成panic。一旦觸發從panic那一行開始同函數里后面的代碼不會再跑。先看沒有recover的情況——進程會直接崩潰packagemainimportfmtfuncnn(aint)(bool,error){ifa!9{panic(請輸入9)// 程序錯誤直接中斷當前執行路徑}fori:1;ia;i{fork:1;ki;k{fmt.Printf(%d * %d %d ,k,i,i*k)}fmt.Println()}returntrue,nil}funcmain(){success,err:nn(8)iferr!nil{fmt.Println(err,執行失敗)}else{fmt.Println(success,執行成功)}// 上面已經 panic這行永遠走不到fmt.Println(其余邏輯)}運行后會看到 panic 棧且沒有「其余邏輯」。這就是和業務錯誤最大的差別。defer 用法defer會把一次函數調用推遲到當前函數即將返回之前執行常用于關文件、解鎖、以及配合recover捕獲panic。基本順序先寫正常邏輯defer的內容最后跑。packagemainimportfmtfuncmain(){fmt.Println(開始)deferfmt.Println(defer收尾)fmt.Println(業務邏輯)}輸出開始 業務邏輯 defer收尾多個defer按后進先出LIFO執行——最后注冊的最先跑packagemainimportfmtfuncmain(){deferfmt.Println(第一)deferfmt.Println(第二)deferfmt.Println(第三)fmt.Println(函數體)}輸出函數體 第三 第二 第一和錯誤處理相關的關鍵點recover必須寫在defer里才有效普通代碼里調用recover()拿不到 panic。defer常用來保證「出錯也要收尾」關文件、釋放鎖、打印現場。參數在寫defer那一刻就算好不是真正執行時才算。更多細節見「Go 函數」一文的 defer 小節。用deferrecover兜住 panic加上recover后進程不會崩潰可以打印 panic 信息但panic發生點之后的代碼仍然不會執行。packagemainimportfmtfuncnn(aint)(bool,error){ifa!9{panic(請輸入9)}fori:1;ia;i{fork:1;ki;k{fmt.Printf(%d * %d %d ,k,i,i*k)}fmt.Println()}returntrue,nil}funcmain(){deferfunc(){ifinfo:recover();info!nil{fmt.Println(捕獲到程序錯誤:,info)}}()success,err:nn(8)// 這里 paniciferr!nil{fmt.Println(err,執行失敗)}else{fmt.Println(success,執行成功)}// panic 之后同函數里這些代碼依然不走fmt.Println(其余邏輯)}輸出類似捕獲到程序錯誤: 請輸入9沒有「執行失敗 / 執行成功」也沒有「其余邏輯」。recover的作用是攔住崩潰不是從 panic 下一行接著跑。怎么選情況建議用戶輸錯、查不到數據、權限不夠返回error庫函數約定用error如os.Open跟著返回 / 包裝error明顯的編程錯誤、不可繼續的狀態可以panic服務入口想避免整個進程掛掉邊界處deferrecover打日志后返回錯誤響應入門階段優先練好if err ! nilpanic/recover留給真正的異常路徑不要拿來代替普通業務校驗。作業返回 error寫divide(a, b int) (int, error)b 0時返回錯誤否則返回商。在main里分別用(10, 2)和(10, 0)調用并處理。業務錯誤對比改寫文中的nn允許傳入19任意整數打印對應乘法表小于1或大于9時返回error。調用失敗后打印一句「其余邏輯」確認它仍會執行。defer 順序在main里注冊三個defer打印A、B、C再打印一句「函數體」觀察輸出順序是否為「函數體 → C → B → A」。panic 與 recover寫一個函數mustPositive(n int)n 0時panic。在main里用deferrecover捕獲打印 panic 信息并在調用后面再寫一句fmt.Println(其余邏輯)驗證它不會執行。綜合練習寫printTable(n int) error業務錯誤版和mustPrintTable(n int)n ! 9就panic。在main里先演示返回error后后續代碼仍運行再用recover演示panic被兜住但后續代碼不運行。