這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
由於年後工作實在太忙,一直也沒寫點什麼。不過這篇我覺得值得,所以……
原文:https://tip.golang.org/hg/doc/go1.1.html
原文連結我進行了替換,現在指向 tip 大多數應該正確吧。不過如果是 Go 1.1 正式發布半年後,我可不保證了。
————翻譯分隔線————
Go 1.1 介紹
Go 第一版(簡稱 Go 1 或 Go 1.0)發佈於 2012 年三月,這個版本提供了穩定的 Go 語言和庫。其穩定性讓全世界 Go 使用者社區和相關係統茁壯成長。從那時起,就發布了若干個“關鍵點”——1.0.1、1.0.2 和 1.0.3。這些點的發布修複了若干已知 bug,但是對於實現本身並沒有進行修改。
這個新的發布版,Go 1.1,在保持相容性的前提下添加了若干重要的(當然,向後相容)語言變化,而庫變化的清單也很長(也向後相容),還有在編譯器、庫和運行時環境實現的主要工作。焦點是效能。測試並不是十分精確,但是對於許多測試程式來說都有著重要的、有時是戲劇性的效能改善。我們相信,通過升級 Go 的安裝包,並且重新編譯,許多使用者的程式也能讓人體會到這一改進。
這一文檔匯總了從 Go 1 到 Go 1.1 的變化。雖然這個發布版有一些極為罕見的錯誤情況,而當這些情況在發生時必須被處理。在 Go 1.1 下運行,幾乎不需要修改任何代碼。下面描述了細節;參閱 64 位元整數和 Unicode 文字的特別說明。
語言的變化
Go 的相容性文檔保證了用 Go 1 語言規範編寫的程式仍然可以使用,並且可以會繼續被維護。儘管有一些細節的錯誤情況已經被指出,但是規範本身的完善還是相當有趣的。同時還增加了一些語言的新特性。
整數除以零
在 Go 1 中,整數被一個常量零整除會產生一個運行時 panic:
func f(x int) int {return x/0}
在 Go 1.1 中,一個整數被一個常量零整除不是合法的程式,因此這會是一個編譯時間錯誤。
代用的 Unicode 文字
細化了 string 和 rune 文字的定義,以便將代用部分排除在合法的 Unicode 編碼值以外。參閱 Unicode 部分瞭解更多資訊。
方法值
現在 Go 1.1 實現了方法值,也就是將函數綁定在特定的接收者的值上。例如,有一個 Writer 的值 w,運算式 w.Write,是一個方法值,作為用於向 w 寫入的函數;這與函數文法中對 w 進行閉包是等價的:
func (p []byte) (n int, err error) {return w.Write(p)}
方法值與方法運算式是不同的,方法運算式從方法中利用指定的類型構造了一個函數;方法運算式 (*bufio.Writer).Write 與第一個參數類型指定為 (*bufio.Writer) 的函數等價:
func (w *bufio.Writer, p []byte) (n int, err error) {return w.Write(p)}
更新:已有代碼不受影響;這個變動是嚴格的向後相容。
Return requirements
在 Go 1.1 之前,一個函數返回一個值必須明確的在函數結束時“return”或調用 panic;這是一個讓程式明確函數的概念的簡單的途徑。但是,顯然有許多情況最後的“return”沒有必要,例如, 一個只有死迴圈“for”的函數。
在 Go 1.1 中,關於最後的“return”語句的規則更加寬鬆。它引入了一個終止語句的概念,它保證了在函數中這個語句總是最後被執行。例如在沒有條件的“for”迴圈中,也沒有“if-else”語句用來在中間通過“return”結束。那麼函數的最後一個語句可以在文法上被認為是終止語句,而不需要最後的“return”語句。
注意這個規則純粹是文法上的:它並不關注代碼中的值,因此也沒有複雜的分析。
更新:這個變動是向後相容的,不過有著多餘“return”語句或調用 panic 的已有代碼可能需要手工處理一下。這些代碼可用 go vet
來標識。
實現和工具的變更
命令列參數解析
在 gc 工具鏈中,編譯器和連結器現在使用與 Go 的 flag
包一致的命令列參數解析規則,而與傳統的 Unix 參數解析背道而馳。這可能會對直接調用工具的指令碼產生影響。例如,go tool 6c -Fw -Dfoo
現在必須寫為 go tool 6c -F -w -D foo
。
在 64 位元平台上的整數大小
該語言允許根據具體實現選擇 int 類型和 uint 類型是 32 或 64 位元的。之前 Go 的實現是在所有系統上都讓 int 和 uint 是 32 位的。現在 gc 和 gccgo 的實現都讓 int 和 uint 在如 AMD64/x86-64 這樣的平台上是 64 位元的。拋開別的不說,單這個就使得 slice 在 64 位元平台上可以分配超過 20 億的元素。
更新:大多數程式不會受到這個的影響。 由於 Go 不允許不同數字類型之間的隱式轉換,不會有程式在編譯時間報錯。然而,那些隱式假設 int 是 32 位的程式,在行為上可能發生變化。例如,這個程式在 64 位元系統中會列印正數,在 32 位系統中會列印負數:
x := ^uint32(0) // x is 0xffffffffi := int(x) // i is -1 on 32-bit systems, 0xffffffff on 64-bitfmt.Println(i)
要保留 32 位的符號(在所有系統上都是 -1)應該用下面的具有可移植性的代碼代替:
i := int(int32(x))
Unicode
為了能夠表達 UTF-16 中超過 65535 的編碼值,Unicode 定義了代用部分,一個僅用於組裝更大的值的編碼值範圍,且僅在 UTF-16 中。在這個代用範圍內的編碼值如果用於其他任何情況都是非法的,如作為 UTF-8 編碼,或作為獨立的 UTF-16 編碼。例如在遇到將一個 rune 轉換成 UTF-8 時,它被當作一個編碼錯誤對待,併產生一個替代的 rune,utf8.RuneError, U+FFFD。
這個程式,
import "fmt"func main() { fmt.Printf("%+q\n", string(0xD800))}
在 Go 1.0 中列印“\ud800”,但在 Go 1.1 中列印“\ufffd”。
半個代用 Unicode 值現在在 rune 和 string 常量中都是非法的,因此如“\ud800”和“\ud800”的常量現在會被編譯器拒絕。當編寫為獨立的 UTF-8 編碼的位元組時,這樣字串還是可以被建立的,例如“\xed\xa0\x80”。然而,當這個字串被作為一個 rune 序列解碼時,比如在 range 迴圈中,它只會產生 utf8.RuneError 值。
Unicode 位元組順序讓 U+FFFE 和 U+FEFF 在 UTF-8 編碼下可以作為 Go 源碼的第一個字元出現。雖然在位元組順序未設定的 UTF-8 編碼中,它是完全不必要的,不過有些編輯器會將其作為“魔法數值”添加進去,用來標識一個 UTF-8 編碼的檔案。
更新:大多數程式不會受到代用變更的影響。基於舊的行為的程式應當通過修改來避免問題。位元組順序標識的變更是嚴格向後相容的。
gc 彙編
基於如 int 到 64 位元和其他一些變化,在 gc 工具鏈的函數參數的棧布局發生了變化。使用彙編編寫的函數至少需要一個 frame 指標位移量。
更新:現在 go vet
命令可以檢查用彙編實現的函數是否匹配 Go 的函數原型。
go 命令的變化
為了讓新的 Go 使用者獲得更好的體驗,go 命令做了若干改動。
首先,當編譯、測試或運行 Go 代碼的時候,go 命令會給出更多的錯誤資訊細節,當一個包無法被定位時,會列出搜尋的路徑清單。
$ go build foo/quxxcan't load package: package foo/quxx: cannot find package "foo/quxx" in any of: /home/you/go/src/pkg/foo/quxx (from $GOROOT) /home/you/src/foo/quxx (from $GOPATH)
其次,go get
命令不再允許下載包源碼時,將 $GOROOT 作為預設的目的路徑。要使用 go get
命令,必須有一個合法的 $GOPATH。
$ GOPATH= go get code.google.com/p/foo/quxxpackage code.google.com/p/foo/quxx: cannot download, $GOPATH not set. For more details see: go help gopath
最後,作為前面變化的結果,go get
命令會在 $GOPATH 和 $GOROOT 設定為相同值的時候報錯。
$ GOPATH=$GOROOT go get code.google.com/p/foo/quxxwarning: GOPATH set to GOROOT (/home/User/go) has no effectpackage code.google.com/p/foo/quxx: cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath
go test 命令的變化
go test
命令在進行效能測試時不再刪除二進位內容,以便更容易的分析效能測試。實現上是在啟動並執行時候設定了 -c 參數。
$ go test -cpuprofile cpuprof.out mypackage
在 go test
運行之後,mypackage.test 將會留在目錄中。
go test
命令現在可以報告 goroutine 在哪裡阻塞的測試資訊,也就是說,它們在哪一直等著某個事件,例如一個 channel 通訊之類的。當用 -blockprofile
開啟 go test
的阻塞測試時,就會展示這些資訊。 運行 go help test
瞭解更多資訊。
go fix 命令的變化
fix 命令通常以 go fix
執行,不再提供從 Go1 之前的版本升級到 Go 1 API 的功能。如果要升級 Go 1 之前的代碼到 Go 1.1,首先應當使用 Go 1.0 的工具鏈,將代碼轉化到 Go 1.0。
效能
用 Go 1.1 的 gc 工具集編譯出來的代碼的效能對於大多數 Go 程式來說應當有顯著的提升。一般來說,與 Go 1.0 相比,大約有 30%-40% 的提升,有時甚至更高,當然也會比這個值低,甚至沒有提升。對於工具和庫來說,有太多的小的效能驅使的改動,以至於無法將它們全部列在這裡。不過下面的主要變更還是有必要留意的:
- gc 編譯器在大多數情況下都會產生較好的代碼,尤其是在 32 位 Intel 架構下的浮點值。
- gc 編譯器做了更多的內連,包括在運行時的一些操作,例如 append 和介面轉換。
- Go 的 map 有了新的實現,在記憶體複製和 CPU 時間上有了重大的改進。
- 記憶體回收實現了更多的並行,這可以降低在多 CPU 環境下啟動並執行程式的延遲。
- 記憶體回收同時也更加精準,這增加了一點 CPU 時間開銷,但是極大的降低了堆的大小,尤其是在 32 位的架構下。
- 通過緊密結合運行時和網路程式庫,在網路操作時需要的環境切換會更少。
標準庫的變化
bufio.Scanner
在 bufio 包中有多種方式擷取文本輸入,ReadBytes、ReadString 和特別的 ReadLine,對於簡單的目的這些都有些過於複雜了。在 Go 1.1 中,添加了一個新類型,Scanner,以便更容易的處理如按行讀取輸入序列或空格分隔的詞等,這類簡單的任務。它終結了如輸入一個很長的有問題的行這樣的輸入錯誤,並且提供了簡單的預設行為:基於行的輸入,每行都剔除分隔標識。這裡的代碼展示來一次輸入一行:
scanner := bufio.NewScanner(os.Stdin)for scanner.Scan() { fmt.Println(scanner.Text()) // Println will add back the final '\n'}if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "reading standard input:", err)}
輸入的行為可以通過一個函數控制,來控制輸入的每個部分(參閱 SplitFunc 的文檔),但是對於複雜的問題或持續傳遞錯誤的,可能還是需要原有介面。
net
在 net 包中的協議特定的解析器之前對傳遞入的網路名稱很寬鬆。雖然文檔明確指出對於 ResolveTCPAddr 合法的網路名稱只有“tcp”,“tcp4”和“tcp6”,Go 1.0 的實現對於任何字串都會接受。而 Go 1.1 的實現,如果網路名稱不在這些字串中,就會返回一個錯誤。這對於其他協議特定的解析器 ResolveIPAddr、ResolveUDPAddr 和 ResolveUnixAddr 也是一樣。
之前的的實現,ListenUnixgram 返回一個 UDPConn 作為接收串連的端點。在 Go 1.1 的實現裡,用 UnixConn 來代替,這允許用它的 ReadFrom 和 WriteTo 方法讀寫。
資料結構 IPAddr、TCPAddr 和 UDPAddr 添加了一個叫做 Zone 的新字串欄位。由於新的欄位,使用沒有標籤的複合文法(例如 net.TCPAddr{ip, port})的代碼代替有標籤的文法(net.TCPAddr{IP: ip, Port: port})會出錯。Go 1 的相容性規則允許這個變化:用戶端代碼必須使用標籤化的文法以避免這種破壞。
更新:為了修正由於新的結構體欄位帶來的破壞,go fix
將會重寫這些類型的代碼以添加標籤。更通用的是,go vet
將會標識出所有應當使用欄位標籤的複合文法。
reflect
reflect 包有若干重大改進。
現在用 reflect 包返回一個“select”語句是可能的;參閱 Select 和 SelectCase 瞭解更多細節。
新的方法 Value.Convert(或 Type.ConvertibleTo)提供了對一個 Value 進行 Go 的轉換和類型斷言操作(或者是檢測這種可能性)的函數方式。
新的函數 MakeFunc 建立了一個使得在已有 Value 上調用函數更加容易的封裝函數,可以用於標準的 Go 參數的轉換,例如將一個 int 傳遞為 interface{}。
最後,新的函數 ChanOf、MapOf 和 SliceOf 可以從已有類型中構造新 Type,例如在僅提供 T 的情況下構造 []T。
time
之前的 time 包在 FreeBSD、Linux、NetBSD、OS X 和 OpenBSD 上精確到微秒。Go 1.1 在這些作業系統上的實現可以精確到納秒。程式用微妙的精確度向外部寫入再讀出,若覆蓋掉原有值的話,將會產生精度的損失。Time 有兩個新方法,Round 和 Truncate,可以用來在向外部儲存寫入前,從時間裡去除精度。
新方法 YearDay 返回指定 time 值在一年中的某天的唯一整數序數。
Timer 類型有一個新方法 Reset,讓定時器在指定的間隔後到期。
最後,一個新函數 ParseInLocation 與已有的 Parse 類似,不過會忽略解析的字串中的時區資訊,而使用傳入的位置(時區)來解析時間。這個函數解決了時間 API 中常見的混亂情況。
更新:對於那些使用更低精度的外部格式來讀寫時間的代碼,應當修改用新的方法。
Exp 舊的代碼樹移動到 go.exp 和 go.text 子版本庫
為了讓使用二進位發布版的使用者在需要的時候訪問更加容易,不包含在二進位發布版的 exp 和舊的源碼樹被移動到新的子版本庫 code.google.com/p/go.exp。舉例來說,如果要訪問 ssa 包,執行
$ go get code.google.com/p/go.exp/ssa
然後在 Go 代碼中,
import "code.google.com/p/go.exp/ssa"
舊的包 exp/norm 也遷移到了新的版本庫 go.text,這裡包含了正在開發的 Unicode API 和其他文本相關的包。
庫的微小變更
下面的清單列出了庫的微小改動,大多數是一些增強。對於每個變更可參閱包相關的文檔瞭解更多資訊。
bytes
包有兩個新函數,TrimPrefix
和 TrimSuffix,含義不言而喻。同樣,
Buffer
類型有一個新方法 Grow,提供了一些控制緩衝內部記憶體配置的能力。最後,
Reader
類型現在有 WriteTo
方法,因此它也實現了 io.WriterTo
介面。
crypto/hmac
有一個新函數,Equal,來比較兩個
MAC。
crypto/x509
包現在支援 PEM 塊(執行個體參閱 DecryptPEMBlock),以及一個新的函數
ParseECPrivateKey
用來解析橢圓曲線私密金鑰。
database/sql
包在 DB 類型上
有了新的 Ping
方法用於檢測串連的健康情況。
database/sql/driver
有了一個新的 Queryer
介面,這樣 Conn
可以通過實現該介面對效能做一些改進。
encoding/json
包的 Decoder
有了新方法 Buffered,以提供訪問在其緩衝內剩餘資料的功能,同樣新方法
UseNumber
會將一個值解碼為其實是字串的新類型 Number,而不是
一個
float64。
encoding/xml
包有了一個新函數 EscapeText,用於輸出
escape 過的 XML,Encoder 的方法
Inden 則專門用於輸出帶縮排的格式。
- 在
go/ast
包中,新類型 CommentMap
和其關聯的方法使得從 Go 程式中分離和處理注釋變得更加容易。
- 在
go/doc
包中,解析器現在可以更好的跟蹤一些如 TODO 這樣的標識,godoc
命令可以根據 -notes 參數選擇過濾或呈現這些資訊。
- 一個新的包,
go/format,為程式提供了更加方便的方式來獲得 gofmt 的格式化的能力。它有兩個函數,
Node
用來格式化 Go 的解析 Node,而
Source
用來格式化 Go 的原始碼。
html/template 包中
沒有文檔並且只部分實現的“noescape”特性被移除;那些依賴它的程式會被破壞。
io
包現在將 io.ByteWriter
介面匯出,用以滿足一次寫一個位元組這樣的常見功能。
log/syslog
包現在更好的提供了系統特定的日誌功能。
math/big
包的 Int
類型現在有了方法 MarshalJSON
和 UnmarshalJSON
用以轉換到或從 JSON 格式轉換。同樣,Int
現在可以通過 Uint64
和 SetUint64
直接轉換到 uint64 或從 uint64 轉換,而 Rat
通過 Float64
and SetFloat64
.
mime/multipart
包的 Writer 有了新的方法,
SetBoundary 用來定義包輸出的邊界分隔。
net
包的 ListenUnixgram
函數修改了傳回值的類型:現在它返回 UnixConn
而不是 UDPConn,這明顯是 Go 1.0 的一個錯誤。因此這個 API 的變更修複了一個 bug,這符合 Go 1 的相容性規則。
net
包包含了一個新函數,DialOpt,為
Dial 增加選項。每個選項都由新的介面
DialOption
體現。新的函數 Deadline、
Timeout、
Network 和
LocalAddress
然會一個 DialOption。
net
增加了帶地區驗證的本地 IPv6 地址的支援,如 fe80::1%lo0。地址結構體
IPAddr、
UDPAddr 和
TCPAddr
將地區資訊記錄在一個新的欄位裡,那些需要字串格式作為地址的函數,例如 Dial、
ResolveIPAddr、
ResolveUDPAddr 和
ResolveTCPAddr 現在接受帶地區驗證的格式。
net
包添加了 LookupNS
作為解析函數。LookupNS
根據主機名稱返回一個 NS records 。
net
包向 IPConn
(ReadMsgIP
和 WriteMsgIP)和
UDPConn
(ReadMsgUDP
和 WriteMsgUDP
)加了指定協議的讀寫方法。還有個 PacketConn 的特別版本的
ReadFrom
和 WriteTo
方法,提供了訪問資料包的帶外資料的能力。
net
為 UnixConn
添加了方法以便半關閉串連(CloseRead
和 CloseWrite),這與
TCPConn 的已有方法匹配。
net/http
包包含了若干新增。ParseTime
解析一個時間字串,會嘗試若干種常見的 HTTP 時間格式。Request 的
PostFormValue 方法與 FormValue 類似,不過忽略了 URL 參數。
CloseNotifier
介面提供了伺服器端處理常式發現用戶端中斷連線的一種機制。ServeMux
類型現在有了 Handler
方法來訪問 Handler
的路徑而不需要執行它。Transport
現在可以通過 CancelRequest
取消一個進行中的請求。最後, 當 Response.Body 在完全被處理之前被關閉的話,T
ransport 現在會對關閉 TCP 串連保持更樂觀的態度。
- 新的
net/http/cookiejar
包提供了基礎的管理 HTTP cookie 的功能。
net/mail
包有了兩個新函數,ParseAddress
和 ParseAddressList,來解析 R
FC 5322 格式化的地址到 Address
結構體。
net/smtp
包的 Client
類型有了一個新的方法,Hello
,用於向伺服器發送 HELO
或 EHLO
訊息。
net/textproto
有兩個新函數,TrimBytes
和 TrimString,用來僅在
ASCII 下進行前後空符的切除。
- 新方法
os.FileMode.IsRegular
讓瞭解一個檔案是否是普通檔案變得更加簡單。
image/jpeg
現在可以讀取預先載入 JPEG 檔案,並且處理某些二次取樣配置資訊。
regexp
包現在通過 Regexp.Longest 可以
支援 Unix 原生的最左最長相符,而 Regexp.Split
使用Regex定義的分離器將字串分解成組的。
runtime/debug
有三個關於記憶體使用量的新函數。FreeOSMemory
函數觸發記憶體回收,並嘗試將未使用的記憶體退回作業系統;ReadGCStats
獲得控制器的統計資訊;而 SetGCPercent
提供了一個可程式化的途徑來控制控制器執行頻率,包括永遠禁止其執行。
sort
包有一個新函數,Reverse。作為調用
sort.Sort
的參數的包裹,通過調用 Reverse
可以讓排序結果反續。
strings
包有兩個新函數,TrimPrefix
和 TrimSuffix
含義不言而喻,還有 Reader.WriteTo
方法,因此 Reader
現在實現了 io.WriterTo
介面。
syscall
包的有許多更新,包括對每個支援的作業系統的系統調用進行加固。
testing
包現在可以在效能測試中使用 AllocsPerRun
函數和 BenchmarkResult 的
AllocsPerOp
方法自動產生記憶體配置統計。還有 Verbose
函數來檢測 -v 的命令列參數狀態,和 testing.B
和 testing.T 的
新方法 Skip
來簡單跳過一些不必要的測試。
- 在
text/template
和 html/template
包中,模板現在可以用圓括弧來對字元序列分組,這簡化了建立複雜的字元序列的過程。TODO:連結到一個執行個體。同時,作為新的解析器的一部分,Node
介面有兩個方法用來提供更好的錯誤報表。這同樣遵循 Go 1 相容性規則,由於這個介面被明確期望只有 text/template
和 html/template 包使用,而其安全機制保證了這點,所以應當
沒有代碼會受到影響。
- 在
unicode/utf8
包中,新函數 ValidRune
報告了一個 rune 是否是一個合法的 Unicode 編碼值。為了確保合法,rune 的值必須在範圍內,且不能為半個代用符。
unicode
包的實現被更新到 Unicode 7.2.0 版本。
————翻譯分隔線————
時運不濟,幾片去痛片壓制下才勉強撐了幾天。只是看來去痛片解決不了發燒問題。回到家被強制休息了……
拖到今天才翻譯完,而且我相信,品質一定很差很差很差……
大家自己看吧!有問題給我留言,我休眠了。
如果明天早上我沒回來,請諸位向 God of Gopher 禮拜,我一定是去了那美麗的地方。