Int Make 居然不是關(guān)鍵字?
本文轉(zhuǎn)載自微信公眾號「董澤潤的技術(shù)筆記」,作者董澤潤。轉(zhuǎn)載本文請聯(lián)系董澤潤的技術(shù)筆記公眾號。
這是一個小白問題,有多少人知道 int 不是關(guān)鍵字?make 也不是關(guān)鍵字?
我們知道每種語言都有關(guān)鍵字和保留字的,而 go 以關(guān)鍵字少著稱,只有25個
- break default func interface select
- case defer go map struct
- chan else goto package switch
- const fallthrough if range type
- continue for import return var
也就是說,我們常用的 make, cap, len不是關(guān)鍵字,就連基本數(shù)據(jù)類型 int, int64, float 也都不是。但是 C 語言中關(guān)鍵字可是非常多的
make 內(nèi)置函數(shù)
- package main
- import "fmt"
- func main(){
- make := func() string {
- return "hijacked"
- }
- int := make() // Completely OK, variable 'int' will be a string
- fmt.Println(int) // Prints "hijacked"
- }
這段代碼 make 變量是一個閉包,返回一個字符串,而 int 變量類型是字符串。最后函數(shù)打印 hijacked. 顯然這段代碼很神經(jīng)病,誰要這么寫會被打死,但確是可以編譯成功的
同時如果想繼續(xù)用 make 創(chuàng)建 map, 或是用 int 聲明變量就會報錯。本質(zhì)上 make, cap, len 都是 go 源碼中的函數(shù)名,有點泛型的意思
- // The make built-in function allocates and initializes an object of type
- // slice, map, or chan (only). Like new, the first argument is a type, not a
- // value. Unlike new, make's return type is the same as the type of its
- // argument, not a pointer to it. The specification of the result depends on
- // the type:
- // Slice: The size specifies the length. The capacity of the slice is
- // equal to its length. A second integer argument may be provided to
- // specify a different capacity; it must be no smaller than the
- // length. For example, make([]int, 0, 10) allocates an underlying array
- // of size 10 and returns a slice of length 0 and capacity 10 that is
- // backed by this underlying array.
- // Map: An empty map is allocated with enough space to hold the
- // specified number of elements. The size may be omitted, in which case
- // a small starting size is allocated.
- // Channel: The channel's buffer is initialized with the specified
- // buffer capacity. If zero, or the size is omitted, the channel is
- // unbuffered.
- func make(t Type, size ...IntegerType) Type
- func len(v Type) int
- func cap(v Type) int
上面是 runtime 中對 make, len, cap 的函數(shù)定義,大家可以看注釋或是看 builtin.go. make 接收三種類型參數(shù):Map, Channel, Slice. 返回值是類型 T, 而不像 new 返回的是指針 *T
也就是說,變量名用 make, 只是在 main 函數(shù)這個詞法塊中普通的局部變量而己,同時遮蔽了 runtime 的 make 函數(shù)名
Predeclared identifiers
前面說的是 make, 那么對于 int 呢?其實道理也一樣,這些都是 go 預定義的標識符 Predeclared identifiers
- Types:
- bool byte complex64 complex128 error float32 float64
- int int8 int16 int32 int64 rune string
- uint uint8 uint16 uint32 uint64 uintptr
- Constants:
- true false iota
- Zero value:
- nil
- Functions:
- append cap close complex copy delete imag len
- make new panic print println real recover
其實這些都 document 在 builtin.go,包括常見的整數(shù)類型,true, false, iota, nil 以及常用的函數(shù) make, new, copy 等等,這些在其它語言可能都對應著關(guān)鍵詞 keywords 或是保留詞
從編譯原理的角度看,identifiers 和 keywords 關(guān)鍵詞沒有本質(zhì)的區(qū)別,都是一個一個 token 而己
官方告訴我們,這些預定義的標識符在 universe block 塊中都是隱式定義的,所以我們才能直接用。那么什么是 universe block 呢?
- Block = "{" StatementList "}" .
- StatementList = { Statement ";" } .
除了上面這種顯示的語句塊,還有很多隱式的語句塊。大家要小心,因為很多時候 variable shadow 就是因為這個隱式的
- The universe block encompasses all Go source text. 通用塊包括 go 源碼文本
- Each package has a package block containing all Go source text for that package. 每個包都有一個塊,包含該包的所有 Go 源代碼
- Each file has a file block containing all Go source text in that file. 每個文件都有一個文件塊,包含該文件中的所有 Go 源碼
- Each "if", "for", and "switch" statement is considered to be in its own implicit block. 每個 if、for 和 switch 語句都被認為是在自己的隱式塊中
- Each clause in a "switch" or "select" statement acts as an implicit block. switch 或 select 語句中的每個子句都是一個隱式塊
我們就犯過錯誤,命中了最后一條導致了變量 shadow. 那么問題來了,為什么 go 選擇預定義標識符的方式,而不是直接定義成 keywords 呢?Go prefers the universal block over keywords because declarations can be added to the universal block without breaking existing programs