這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
目錄 [−]
- 標籤範圍
- 空標識符和預定義標識符
- 輸出標識符
- iota
- 函式宣告
- 方法聲明
本文介紹Go的聲明和範圍。
聲明用於常量、變數、類型、函數、標籤和包。
每個非空標識符(identifier)必須聲明。同一代碼塊中標識符只能聲明一次。標識符不能同時聲明在檔案代碼塊和包代碼塊中。
空標識符為底線 "_",它可以做為匿名預留位置。
一個聲明的標識符的範圍總結如下:
- 預聲明的標識符的範圍是全域代碼塊如
int、true等
- 頂層的(函數之外)的標識符, 用來聲明常量、類型、變數或者函數(非方法), 這些標識符的範圍是包代碼塊
- 一個輸入的包的包名的範圍是這個檔案的檔案代碼塊
- 用來表示方法 receiver、函數參數、結果變數的標識符的範圍是函數體
- 在一個函數內聲明的常量或者變數的標識符的範圍起於ConstSpec 、 VarSpec之後(也就是標識符聲明之後), 結束於包含它的最內層的代碼塊尾部
- 函數內聲明的類型標識符始於 TypeSpec, 結束於包含它的最內層的代碼塊尾部
代碼塊中的標識符可以在內部的代碼塊中再次聲明,但是內部的標識符和外部的標識符表示不同的對象,這個一定小心。
package clause並不是聲明語句。包名不能出現在任何範圍中, 經常我們為了方便,聲明一個變數的時候和包名一樣,比如在引入net/http包的時候聲明一個變數是http,這是不對的,這會導致變數名會隱藏(shadow)包名。
標籤範圍
標籤(label)範圍可以用於break、continue、goto語句,這在其它語言中也有定義,儘管不會推薦廣泛應用。
1234567 |
i := 0回來:i++fmt.Println(i)if i < 5 { goto 回來} |
定義一個未被使用的標籤是非法的,這和函數內的定義未被使用的變數是一樣的。
標籤不會和其它同名的標識符衝突。
標籤的範圍只在聲明它的位置到函數體的尾部,不會作用於內部嵌套的函數。
空標識符和預定義標識符
空標識符前面已經提到了,下面是一些空標識符的應用。
123456789 |
import (_ "net/http")const _ = "hello world"var _ = 100type _ []inttype _ struct{}type _ interface{} |
預定義的標識符第一章中已經介紹了。
輸出標識符
Go語言中沒有 public、protect、private等關鍵字,如果想在其它包中訪問當前包的標識符,
標識符應該具備下面兩個條件,這和其它程式設計語言不太一樣:
1、標識符名字的第一個字元應該是unicode upper case letter (Lu)
2、並且標識符聲明在包代碼塊中,或者它是一個欄位名或者方法名。
舉個例子。
在當前的main程式所在的目錄中建立一個目錄p,在裡面建立一個檔案t.go,定義兩個包範圍的變數:
1234 |
package pvar str = "hello world"var Str = "Hello World" |
然後在main程式中嘗試訪問package p下的這兩個變數,會發現只能訪問Str,str報錯,說沒有匯出:
123456789101112 |
package mainimport ("fmt""./p")func main() {fmt.Println(p.str)fmt.Println(p.Str)} |
函數、常量、類型、struct、interface、方法都遵循這個法則。所以要想匯出一個標識符,請首字母大寫。
注意,這裡要求的是首字母是unicode upper case letter,就是unicode分類為Lu的字元,對於Unicode字元,你需要知道哪些是大寫字元,比如希臘字元也是有大小寫,中文不是大小寫:
12 |
var 一二三 = "123" //未輸出var Π = 3.1415926 //輸出 |
你可以在 這裡 查看unicode 大寫字元列表。
iota
在常量定義中,iota 代表一個連續的未指定類型的整數常量。每當保留字const出現的時候,它都重設為0,後續的每個常量定義都會把它加一。 它常用來定義一組類似枚舉的類型,比如月份、星期、顏色等。
1234567891011121314151617181920 |
const ( // iota is reset to 0c0 = iota // c0 == 0c1 = iota // c1 == 1c2 = iota // c2 == 2)const ( // iota 重設為 0a = 1 << iota // a == 1b = 1 << iota // b == 2c = 3 // c == 3 (iota 雖然沒有使用,但是還是會增加)d = 1 << iota // d == 8)const ( // iota 重設為 0u = iota * 42 // u == 0 (untyped integer constant)v float64 = iota * 42 // v == 42.0 (float64 constant)w = iota * 42 // w == 84 (untyped integer constant))const x = iota // x == 0 (iota 重設為 0)const y = iota // y == 0 (iota 重設為 0) |
如果使用運算式列表,同一運算式的iota的值是相同的,因為只有遇到新的ConstSpec才會增加。
123456 |
const (bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0, iota = 0bit1, mask1 // bit1 == 2, mask1 == 1, iota = 1_, _ // skips iota == 2bit3, mask3 // bit3 == 8, mask3 == 7, iota =3) |
Go語言中沒有枚舉類型,所以一般通過下面的方式定義枚舉類型:
1234567891011121314151617181920 |
type Week intvar names = [...]string{"星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"}const (Monday Week = iota + 1TuesdayWednesdayThursdayFridaySaturdaySunday)func (w Week) String() string {if w > 0 && w < 8 {return names[w-1]}return "非法的星期名"} |
函式宣告
函數可以聲明為簽名,也可以定義方法體。
沒有方法體的函數只有簽名的函數可以聲明在介面中,也可以聲明包代碼塊中,此時標明方法是由外部實現的,比如組合語言。
如果函式宣告了傳回型別,那麼方法體中必須有相應的return語句。
匿名函數又叫函數字面量,它不包含函數名,可以用來賦值給變數(比如在一個函數內或者外賦值給一個變數)、或者直接調用(比如go語句中)。
123 |
f := func(x, y int) int { return x + y }func(ch chan int) { ch <- ACK }(replyChan)go func(ch chan int) { ch <- ACK }(replyChan) |
方法聲明
方法聲明類似函式宣告,但是它包含一個接受者receiver。
在方法名的前面要聲明一個額外的參數,這個參數是單一的,不可變的,作為receiver,
它的類型為T或者*T,T叫做receiver base type。
注意T不能是指標類型或者皆苦類型,並且必須和方法聲明在同一個包下。你想在自己的包下為標準庫中的類型定義一個方法是不行的。
方法名僅僅在類型T 或者 *T的 selector中顯示。
receiver名不能和參數名或者返回參數重名:
1234 |
//錯誤func (i IntArr) Say(i int) (i int) {return 0} |
方法名唯一。
對於struct類型,方法名和欄位名必須唯一。
方法的特性和selector在下一章的運算式介紹。