深入Go語言 - 9 內建函數、匯入、panic

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

目錄 [−]

  1. 內建函數
  2. import
  3. 包的初始化
  4. error、panic和recover

本章介紹Go語言的其它部分,算是Go語言規範的深入學習的收尾工作。

內建函數

前面幾章我們已經認識了幾個內建的函數,這裡我們將所有的內建函數羅列一下。

如果你查看它們的定義的話,你可以訪問builtin/builtin.go,但這些內建的函數只有聲明,沒有方法體,它們不是標準的Go類型,你不能把它們作為函數值進行參數傳遞,只能出現在調用運算式中。事實上這些內建類型並不真的在builtin包中,只是為了產生文檔的需要。

你可以在任意的包中調用這些內建函數,不必引入特定的包如"builtin"。

1、close: 關閉channel
2、len(s):得到字串、數組、數值指標、slice、map、chan的長度
3、cap(s):得到數組、數組指標的長度,得到slice、channel的容量
4、new(T): 組建類型T的零值指標,注意它返回的是指標 *T
5、make: 產生slice、map、channel對象

調用              類型 T     結果make(T, n)       slice      slice of type T with length n and capacity nmake(T, n, m)    slice      slice of type T with length n and capacity mmake(T)          map        map of type Tmake(T, n)       map        map of type T,初始可以容納 n 元素的空間make(T)          channel    不帶緩衝的channel of type T,比如我們在聲明訊號channel的時候make(T, n)       channel    帶緩衝的 channel of type T, 緩衝大小為 n

6、append(s S, x ...T) S: 增加0到n個元素到slice中,返回新的slice
7、copy(dst, src []T) int: 複製源src slice的元素到目標dst slice中,返回複製的元素的數量n, n是src和dst長度的最小值。字串也可以作為src,但是T的類型必須是byte
8、delete(m,k): 刪除map中的一個映射, m為nil或者m[k]不存在也不會panic,而是一個空操作
9、complexrealimag: 複數操作
10、panicrecover: 報告panic和處理panic,後面講
11、printprintln: 盡量不用這兩個函數,因為保證將來它們還會留在Go語言中,使用fmt.Print、fmt.Println

Go的代碼檔案中都會有包(package)的定義,在import聲明的前面。

同一個檔案夾下的所有的檔案都要使用同一個包名(當然,你如果單獨編譯每一個檔案的話,可以不遵守,但是要編譯整個項目,必須遵守)。

但是測試檔案可以叫另外的包名,比如正常代碼的包名為"package abc",測試代碼和範例程式碼的包名為"package abc_test". Go標準庫中混用了這兩種風格。

main包是一個特殊的包,必須聲明一個main函數,main函數無參數,無傳回值,它用來建立可執行程式。

包名不一定和檔案夾的名字保持一致,經常我們的項目的名稱很長,不太適合做包名,所以包名可以用一個簡短的名稱,但是如果可能,盡量用一樣的名字,這樣在下載庫的時候就能直到包的名字了。

import

import用來引入所需要的類型,允許你訪問另外的包中的匯出類型。

以下四種形式都是可以接受的mport:

Import declaration          Local name of Sinimport   "lib/math"         math.Sinimport m "lib/math"         m.Sinimport . "lib/math"         Sinimport _ "lib/math"

為了避免同名的包名衝突,你可以為匯入的包名起一個名字,比如上例中的"m"。
你也可以使用.,這樣無需包名標識符,可以直接使用這個包下的匯出類型。
最後一個情況是使用空標識符,主要是想利用包的初始化,而不是使用它的匯出類型。

import不能匯入包自己,不管是直接的還是間接的(循環參考)。你也不能直接匯入一個包而不使用它的匯出類型,所幸一些工具可以自動幫我們修正import的錯誤,或者自動幫我們匯入,比如goimports。

import可以匯入相對路徑,如"import \"../foo/bar\"",但是強烈你不要這麼做,這不是常用的匯入風格。

import可以用小括弧括起來匯入多個包。

包的初始化

包變數的初始化順序和它們聲明的順序一致,但是也得考慮它們的依賴。
循環相依性初始化也不可以:

12345
package abcvar i int = jvar j int = kvar k int = i

包變數的初始化之後可以調用一個init函數實現其它的初始化過程,你可以顯示地定義這個函數,它可以出現在同一個包下的多個檔案中,執行的順序由編譯器決定。
如果包匯入了其它包,則匯入的包會先初始化。如果多個包都匯入同一個包,則匯入的包只被初始化一次。

error、panic和recover

Go預定義了error類型,雖然它的首字母不是大寫的,但是確可以在任何包下使用:

123
type error interface {Error() string}

而errors包定義了產生簡單Error的方法errors.New(text String) error。如果你想自訂Error類型,你可以實現error介面。

運行時Error,比如數組索引越界會觸發一個運行時的panic,它等價調用panic函數,panic的值是一個runtime.Error:

123456
package runtimetype Error interface {error// and perhaps other methods}

而panic的處理是在一個defer函數中執行的:

12345678910111213141516
func main() {defer func() {fmt.Println("foo")}()defer func() {if x := recover(); x != nil {fmt.Println(x)}}()defer func() {fmt.Println("bar")}()panic("trigger panic")  fmt.Println("end")}

注意defer的執行順序,前面已經講過,和它們聲明的順序相反,所以輸出結果為:

123
bartrigger panicfoo

最後一句沒有機會繼續執行,因為recover執行完後函數就終止了。
這帶來一個問題,如果函數有傳回值,recover後函數的傳回值是什嗎?

12345678910111213141516171819202122
func main() {i := z()fmt.Println(i)}func z() int {defer func() {fmt.Println("foo")}()defer func() {if x := recover(); x != nil {fmt.Println(x)}}()defer func() {fmt.Println("bar")}()panic("trigger panic")fmt.Println("end")return 100}

輸出結果是:

1234
bartrigger panicfoo0

可以函數的返回是傳回型別的零值。

當然說零值也不完全正確,如果函數有命名的返回參數,並且命名的返回參數在panic之前賦值了的話,返回的結果還是最後的賦值結果,下面的代碼中函數的返回結果為50:

1234567891011121314151617181920212223
func main() {i := z()fmt.Println(i)}func z() (r int) {defer func() {fmt.Println("foo")}()defer func() {if x := recover(); x != nil {fmt.Println(x)}}()defer func() {fmt.Println("bar")}()r = 50panic("trigger panic")fmt.Println("end")return 100}

panic如果沒有處理,會傳遞給它的調用者,這就是panic的bubble。當然如果可以預見panic,最好的處理方式就是在本函數內進行處理,因為你不能控制外部調用者的行為,而且外部調用者不一定知道會有panic發生:

123456789101112131415161718192021222324252627
func main() {defer func() {if x := recover(); x != nil {fmt.Println("recover from main:", x)}}()f1()}func f1() {fmt.Println("start f1")f2()fmt.Println("end f1")}func f2() {fmt.Println("start f2")f3()fmt.Println("end f2")}func f3() {fmt.Println("start f3")panic("triggered from f3")fmt.Println("end f3")}

上面的代碼輸出:

1234
start f1start f2start f3recover from main: triggered from f3

如果連main都沒有 recover,則程式異常退出。

還有一個不太引人注意的地方就是如果在recover中產生panic會怎麼樣?還是看例子:

123456789101112131415161718192021222324
func main() {defer func() {if x := recover(); x != nil {fmt.Println("recover from main:", x)}}()f1()}func f1() {defer func() {if x := recover(); x != nil {fmt.Println("recover from f1:", x)panic("triggered from f1")}}()f2()}func f2() {panic("triggered from f2")}

函數f2產生的panic會被函數f1處理, f1在recover的過程中產生一個新的panic,這個panic會把它的調用者main捕獲。
所以recover產生的panic會往上傳遞。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.