mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 19:54:21 +08:00
回到简体
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
## 5.9. Panic異常
|
||||
## 5.9. Panic异常
|
||||
|
||||
Go的類型繫統會在編譯時捕獲很多錯誤,但有些錯誤隻能在運行時檢査,如數組訪問越界、空指針引用等。這些運行時錯誤會引起painc異常。
|
||||
Go的类型系统会在编译时捕获很多错误,但有些错误只能在运行时检查,如数组访问越界、空指针引用等。这些运行时错误会引起painc异常。
|
||||
|
||||
一般而言,當panic異常發生時,程序會中斷運行,併立卽執行在該goroutine(可以先理解成線程,在第8章會詳細介紹)中被延遲的函數(defer 機製)。隨後,程序崩潰併輸出日誌信息。日誌信息包括panic value和函數調用的堆棧跟蹤信息。panic value通常是某種錯誤信息。對於每個goroutine,日誌信息中都會有與之相對的,發生panic時的函數調用堆棧跟蹤信息。通常,我們不需要再次運行程序去定位問題,日誌信息已經提供了足夠的診斷依據。因此,在我們填寫問題報告時,一般會將panic異常和日誌信息一併記録。
|
||||
一般而言,当panic异常发生时,程序会中断运行,并立即执行在该goroutine(可以先理解成线程,在第8章会详细介绍)中被延迟的函数(defer 机制)。随后,程序崩溃并输出日志信息。日志信息包括panic value和函数调用的堆栈跟踪信息。panic value通常是某种错误信息。对于每个goroutine,日志信息中都会有与之相对的,发生panic时的函数调用堆栈跟踪信息。通常,我们不需要再次运行程序去定位问题,日志信息已经提供了足够的诊断依据。因此,在我们填写问题报告时,一般会将panic异常和日志信息一并记录。
|
||||
|
||||
不是所有的panic異常都來自運行時,直接調用內置的panic函數也會引發panic異常;panic函數接受任何值作爲參數。當某些不應該發生的場景發生時,我們就應該調用panic。比如,當程序到達了某條邏輯上不可能到達的路徑:
|
||||
不是所有的panic异常都来自运行时,直接调用内置的panic函数也会引发panic异常;panic函数接受任何值作为参数。当某些不应该发生的场景发生时,我们就应该调用panic。比如,当程序到达了某条逻辑上不可能到达的路径:
|
||||
|
||||
```Go
|
||||
switch s := suit(drawCard()); s {
|
||||
@@ -17,7 +17,7 @@ switch s := suit(drawCard()); s {
|
||||
}
|
||||
```
|
||||
|
||||
斷言函數必須滿足的前置條件是明智的做法,但這很容易被濫用。除非你能提供更多的錯誤信息,或者能更快速的發現錯誤,否則不需要使用斷言,編譯器在運行時會幫你檢査代碼。
|
||||
断言函数必须满足的前置条件是明智的做法,但这很容易被滥用。除非你能提供更多的错误信息,或者能更快速的发现错误,否则不需要使用断言,编译器在运行时会帮你检查代码。
|
||||
|
||||
```Go
|
||||
func Reset(x *Buffer) {
|
||||
@@ -28,11 +28,11 @@ func Reset(x *Buffer) {
|
||||
}
|
||||
```
|
||||
|
||||
雖然Go的panic機製類似於其他語言的異常,但panic的適用場景有一些不同。由於panic會引起程序的崩潰,因此panic一般用於嚴重錯誤,如程序內部的邏輯不一致。勤奮的程序員認爲任何崩潰都表明代碼中存在漏洞,所以對於大部分漏洞,我們應該使用Go提供的錯誤機製,而不是panic,盡量避免程序的崩潰。在健壯的程序中,任何可以預料到的錯誤,如不正確的輸入、錯誤的配置或是失敗的I/O操作都應該被優雅的處理,最好的處理方式,就是使用Go的錯誤機製。
|
||||
虽然Go的panic机制类似于其他语言的异常,但panic的适用场景有一些不同。由于panic会引起程序的崩溃,因此panic一般用于严重错误,如程序内部的逻辑不一致。勤奋的程序员认为任何崩溃都表明代码中存在漏洞,所以对于大部分漏洞,我们应该使用Go提供的错误机制,而不是panic,尽量避免程序的崩溃。在健壮的程序中,任何可以预料到的错误,如不正确的输入、错误的配置或是失败的I/O操作都应该被优雅的处理,最好的处理方式,就是使用Go的错误机制。
|
||||
|
||||
考慮regexp.Compile函數,該函數將正則表達式編譯成有效的可匹配格式。當輸入的正則表達式不合法時,該函數會返迴一個錯誤。當調用者明確的知道正確的輸入不會引起函數錯誤時,要求調用者檢査這個錯誤是不必要和纍贅的。我們應該假設函數的輸入一直合法,就如前面的斷言一樣:當調用者輸入了不應該出現的輸入時,觸發panic異常。
|
||||
考虑regexp.Compile函数,该函数将正则表达式编译成有效的可匹配格式。当输入的正则表达式不合法时,该函数会返回一个错误。当调用者明确的知道正确的输入不会引起函数错误时,要求调用者检查这个错误是不必要和累赘的。我们应该假设函数的输入一直合法,就如前面的断言一样:当调用者输入了不应该出现的输入时,触发panic异常。
|
||||
|
||||
在程序源碼中,大多數正則表達式是字符串字面值(string literals),因此regexp包提供了包裝函數regexp.MustCompile檢査輸入的合法性。
|
||||
在程序源码中,大多数正则表达式是字符串字面值(string literals),因此regexp包提供了包装函数regexp.MustCompile检查输入的合法性。
|
||||
|
||||
```Go
|
||||
package regexp
|
||||
@@ -46,13 +46,13 @@ func MustCompile(expr string) *Regexp {
|
||||
}
|
||||
```
|
||||
|
||||
包裝函數使得調用者可以便捷的用一個編譯後的正則表達式爲包級别的變量賦值:
|
||||
包装函数使得调用者可以便捷的用一个编译后的正则表达式为包级别的变量赋值:
|
||||
|
||||
```Go
|
||||
var httpSchemeRE = regexp.MustCompile(`^https?:`) //"http:" or "https:"
|
||||
```
|
||||
|
||||
顯然,MustCompile不能接收不合法的輸入。函數名中的Must前綴是一種針對此類函數的命名約定,比如template.Must(4.6節)
|
||||
显然,MustCompile不能接收不合法的输入。函数名中的Must前缀是一种针对此类函数的命名约定,比如template.Must(4.6节)
|
||||
|
||||
```Go
|
||||
func main() {
|
||||
@@ -65,7 +65,7 @@ func f(x int) {
|
||||
}
|
||||
```
|
||||
|
||||
上例中的運行輸出如下:
|
||||
上例中的运行输出如下:
|
||||
|
||||
```
|
||||
f(3)
|
||||
@@ -76,7 +76,7 @@ defer 2
|
||||
defer 3
|
||||
```
|
||||
|
||||
當f(0)被調用時,發生panic異常,之前被延遲執行的的3個fmt.Printf被調用。程序中斷執行後,panic信息和堆棧信息會被輸出(下面是簡化的輸出):
|
||||
当f(0)被调用时,发生panic异常,之前被延迟执行的的3个fmt.Printf被调用。程序中断执行后,panic信息和堆栈信息会被输出(下面是简化的输出):
|
||||
|
||||
```
|
||||
panic: runtime error: integer divide by zero
|
||||
@@ -92,9 +92,9 @@ main.main()
|
||||
src/gopl.io/ch5/defer1/defer.go:10
|
||||
```
|
||||
|
||||
我們在下一節將看到,如何使程序從panic異常中恢複,阻止程序的崩潰。
|
||||
我们在下一节将看到,如何使程序从panic异常中恢复,阻止程序的崩溃。
|
||||
|
||||
爲了方便診斷問題,runtime包允許程序員輸出堆棧信息。在下面的例子中,我們通過在main函數中延遲調用printStack輸出堆棧信息。
|
||||
为了方便诊断问题,runtime包允许程序员输出堆栈信息。在下面的例子中,我们通过在main函数中延迟调用printStack输出堆栈信息。
|
||||
|
||||
```Go
|
||||
gopl.io/ch5/defer2
|
||||
@@ -109,7 +109,7 @@ func printStack() {
|
||||
}
|
||||
```
|
||||
|
||||
printStack的簡化輸出如下(下面隻是printStack的輸出,不包括panic的日誌信息):
|
||||
printStack的简化输出如下(下面只是printStack的输出,不包括panic的日志信息):
|
||||
|
||||
```
|
||||
goroutine 1 [running]:
|
||||
@@ -127,4 +127,4 @@ main.main()
|
||||
src/gopl.io/ch5/defer2/defer.go:15
|
||||
```
|
||||
|
||||
將panic機製類比其他語言異常機製的讀者可能會驚訝,runtime.Stack爲何能輸出已經被釋放函數的信息?在Go的panic機製中,延遲函數的調用在釋放堆棧信息之前。
|
||||
将panic机制类比其他语言异常机制的读者可能会惊讶,runtime.Stack为何能输出已经被释放函数的信息?在Go的panic机制中,延迟函数的调用在释放堆栈信息之前。
|
||||
|
||||
Reference in New Issue
Block a user