這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
聲明與賦值
在 Go 語言中,聲明一個變數可以通過 var name type = expression
做到,但同時,Go 也支援稱為短變數聲明的形式 name := expression
。
出於方便的因素,Go 支援多重短變數聲明 name, age := expression1, expression2
,這個用法容易同多重變數賦值混淆 name, age = expression1, expression2
。
另外,也存在一個 new
函數,通過 new(T)
來建立一個 T
類型的對象。
如果使用多重短變數聲明,一旦在同一範圍內該變數已經聲明過,則 Go 語言實際上會執行一個賦值操作,下面舉個例子。
package mainimport ( "fmt")var name string // 包級變數 namefunc main() { fmt.Print(&name) // 列印包級變數 name 的地址 var score int // 聲明一個 score 的 int 變數 name, score := "Wolther47", 100 // 這一行中,有兩個要點: // 1. score:score 在之前被聲明為一個 int 變數,則對於變數 score,`:=` 相當於一個指派陳述式 // 2. name:如果有一個變數在當前的範圍中,沒有進行過聲明,則 Go 就會進行聲明,而不是向上尋找, // 即,此處的 name 會覆蓋包層級變數 name. fmt.Printf("%s's score is %d.\n", name, score) fmt.Print(&name)}
隨即引出的問題是,Go 語言如何界定所謂的範圍。
這是一個比較重要的問題,由於 Go 會在編譯時間檢查每個變數是否使用,一旦出現未使用的變數,就會失敗。如果不能理解範圍,有可能會帶來一些不明所以的編譯錯誤。
在 Blocks and Scopes in Golang 這篇文章裡,作者列出了 Go 的四種代碼塊:
- universe block,包含項目的所有原始碼
- package block,包含項目的所有原始碼,除匯入的包
- file block,包含該檔案內所有的原始碼,包括匯入的包
- lexical block[1],包含在花括弧內的原始碼,但是 composite literals[2] 以及類型定義 type definitions 不構成 lexical block
對於 lexial block,有一些例外:
- 函數的傳入參數,以及 return 的結果,儘管在函數體的花括弧外,但是它們的範圍視為函數體內部
- 選擇、迴圈語句的
if
,switch
以及 for
會開啟兩個 lexical block,一個為顯式,另一個為隱式,隱式的範圍嵌套顯式的範圍。原文此處講的比較抽象,下面以 if
為例直接上代碼
package mainimport ( "fmt")type Cat struct { Name string Age int Servant string}func getCat() Cat { return Cat{"Sushi", 3, "Wolther47"}}func main() { if cat := getCat(); cat.Servant == "Wolther47" { // cat 這個變數的範圍從 if 關鍵詞開始,一直到最後的花括弧結束 words := "Yep" // words 這個變數的範圍只在花括弧內 fmt.Printf("I've served %s for %d years.\n%s\n", cat.Name, cat.Age, words) }}
else
同樣也會開啟一個 lexical block,並且顯然,這個 block 應該和 if 顯式開啟的 lexical block 為同級兄弟關係,並且同樣被嵌套在 if
的隱式範圍裡
switch
的 case
(或者 default
)同樣也會開啟一個 lexical block,嵌套在 switch
隱式開啟的範圍裡
另外,強推這篇文章,作者比較詳盡地歸納了 block,同時也整理了 block 的嵌套以及不同元素該在何處聲明的問題,此處不再贅述。
本文發布在 Wolther47 的部落格上,本作品採用知識共用署名 4.0 國際許可協議進行許可
這篇文章的作者使用 local block 來表示,本文遵循 The Go Programming Language,依舊使用 lexical block
機械工業出版社的 The Go Programming Language 翻譯成「複合字面量」,個人不喜歡這種翻譯,不予採用