Go 1.6 釋出說明
Go 1.6 簡介
最新的 Go 版本 1.6 在 1.5 釋出六個月後到來。其大部分更改都集中在語言、執行時和庫的實現上。語言規範沒有變化。一如既往,此版本保持了 Go 1 相容性承諾。我們預計幾乎所有 Go 程式都將像以前一樣繼續編譯和執行。
此版本新增了對 64 位 MIPS 上的 Linux 和 32 位 x86 上的 Android 的支援;定義並強制執行了 與 C 共享 Go 指標的規則;透明、自動的 HTTP/2 支援;以及一種新的 模板重用機制。
語言變化
此版本沒有語言更改。
移植
Go 1.6 新增了對 64 位 MIPS 上的 Linux (linux/mips64 和 linux/mips64le) 的實驗性支援。這些埠支援 cgo,但僅限於內部連結。
Go 1.6 還新增了對 32 位 x86 上的 Android (android/386) 的實驗性支援。
在 FreeBSD 上,Go 1.6 預設使用 clang 而不是 gcc 作為外部 C 編譯器。
在小端序 64 位 PowerPC 上的 Linux (linux/ppc64le) 上,Go 1.6 現在支援外部連結的 cgo,並且功能大致完整。
在 NaCl 上,Go 1.5 需要 SDK 版本 pepper-41。Go 1.6 新增了對更高 SDK 版本的支援。
在 32 位 x86 系統上,使用 -dynlink 或 -shared 編譯模式時,暫存器 CX 現在會被某些記憶體引用覆蓋,應避免在手寫的彙編程式碼中使用。有關詳細資訊,請參閱 彙編文件。
工具
Cgo
cgo 有一個主要更改,以及一個次要更改。
主要更改是定義了與 C 程式碼共享 Go 指標的規則,以確保此類 C 程式碼可以與 Go 的垃圾回收器共存。簡而言之,Go 和 C 可以在作為 cgo 呼叫的一部分將指向 Go 分配記憶體的指標傳遞給 C 時共享 Go 分配的記憶體,前提是記憶體本身不包含指向 Go 分配記憶體的指標,並且 C 在呼叫返回後不保留該指標。這些規則在程式執行期間由執行時檢查:如果執行時檢測到違規,它會列印診斷資訊並使程式崩潰。這些檢查可以透過設定環境變數 GODEBUG=cgocheck=0 來停用,但請注意,絕大多數被檢查識別出的程式碼都以這樣或那樣的方式與垃圾回收微妙地不相容。停用檢查通常只會導致更神秘的故障模式。強烈建議修復相關程式碼,而不是關閉檢查。有關詳細資訊,請參閱 cgo 文件。
次要更改是新增了顯式 C.complexfloat 和 C.complexdouble 型別,與 Go 的 complex64 和 complex128 不同。與其他數值型別一樣,C 的複數型別和 Go 的複數型別不再可以互換。
編譯器工具鏈
編譯器工具鏈基本保持不變。在內部,最重要的變化是解析器現在是手寫的,而不是從 yacc 生成的。
編譯器、連結器和 go 命令新增了一個 -msan 標誌,類似於 -race,並且只在 linux/amd64 上可用,它支援與 Clang MemorySanitizer 的互動。這種互動主要用於測試包含可疑 C 或 C++ 程式碼的程式。
連結器新增了一個 -libgcc 選項,用於在連結 cgo 程式碼時設定 C 編譯器支援庫的預期位置。該選項僅在使用 -linkmode=internal 時才會被考慮,並且可以設定為 none 以停用支援庫的使用。
Go 1.5 中開始的構建模式實現 已擴充套件到更多系統。此版本新增了對 android/386、android/amd64、android/arm64、linux/386 和 linux/arm64 上的 c-shared 模式的支援;對 linux/386、linux/arm、linux/amd64 和 linux/ppc64le 上的 shared 模式的支援;以及對 android/386、android/amd64、android/arm、android/arm64、linux/386、linux/amd64、linux/arm、linux/arm64 和 linux/ppc64le 上的新 pie 模式(生成位置無關的可執行檔案)的支援。有關詳細資訊,請參閱 設計文件。
提醒一下,連結器的 -X 標誌在 Go 1.5 中發生了變化。在 Go 1.4 及更早版本中,它接受兩個引數,例如:
-X importpath.name value
Go 1.5 增加了一種替代語法,使用一個引數,該引數本身是一個 name=value 對
-X importpath.name=value
在 Go 1.5 中,舊語法仍然被接受,但會列印警告,建議使用新語法。Go 1.6 繼續接受舊語法並列印警告。Go 1.7 將取消對舊語法的支援。
Gccgo
GCC 和 Go 專案的釋出計劃不一致。GCC 5 版本包含 Go 1.4 版本的 gccgo。下一個版本 GCC 6 將包含 Go 1.6.1 版本的 gccgo。
Go 命令
go 命令的基本操作沒有改變,但有許多值得注意的更改。
Go 1.5 引入了實驗性的 vendoring 支援,透過將 GO15VENDOREXPERIMENT 環境變數設定為 1 啟用。Go 1.6 保留了 vendoring 支援,不再認為是實驗性的,並預設啟用。可以透過將 GO15VENDOREXPERIMENT 環境變數設定為 0 來顯式停用。Go 1.7 將取消對該環境變數的支援。
預設啟用 vendoring 最可能導致的問題發生在包含現有名為 vendor 目錄的原始碼樹中,該目錄不希望根據新的 vendoring 語義進行解釋。在這種情況下,最簡單的修復方法是將目錄重新命名為 vendor 以外的任何名稱,並更新任何受影響的匯入路徑。
有關 vendoring 的詳細資訊,請參閱 go 命令 和 設計文件 的文件。
新增了一個構建標誌 -msan,它編譯 Go 以支援 LLVM 記憶體清理器。這主要用於連結到正在使用記憶體清理器檢查的 C 或 C++ 程式碼時。
Go doc 命令
Go 1.5 引入了 go doc 命令,它允許僅使用包名引用包,例如 go doc http。在 Go 1.5 中,如果存在歧義,則使用詞典順序最早的匯入路徑的包。在 Go 1.6 中,透過優先選擇元素較少的匯入路徑來解決歧義,並使用詞典比較來打破平局。這一變化的一個重要影響是現在優先選擇包的原始副本而不是 vendored 副本。成功的搜尋也往往執行得更快。
Go vet 命令
go vet 命令現在會診斷將函式或方法值作為引數傳遞給 Printf 的情況,例如在預期 f() 的地方傳遞 f。
效能
一如既往,這些變化非常普遍和多樣,因此很難對效能做出精確的說明。有些程式可能會執行得更快,有些則會更慢。平均而言,Go 1 基準測試套件中的程式在 Go 1.6 中比在 Go 1.5 中執行速度快百分之幾。垃圾回收器的暫停時間甚至比 Go 1.5 更短,特別是對於使用大量記憶體的程式。
在 compress/bzip2、compress/gzip、crypto/aes、crypto/elliptic、crypto/ecdsa 和 sort 包的實現方面,已進行了重大最佳化,帶來了超過 10% 的改進。
標準庫
HTTP/2
Go 1.6 在 net/http 包中增加了對新 HTTP/2 協議 的透明支援。Go 客戶端和伺服器在使用 HTTPS 時將自動酌情使用 HTTP/2。沒有針對 HTTP/2 協議處理細節的匯出 API,就像沒有針對 HTTP/1.1 的匯出 API 一樣。
必須停用 HTTP/2 的程式可以透過將 Transport.TLSNextProto(對於客戶端)或 Server.TLSNextProto(對於伺服器)設定為非空、空對映來停用。
需要調整 HTTP/2 協議特定細節的程式可以匯入並使用 golang.org/x/net/http2,特別是其 ConfigureServer 和 ConfigureTransport 函式。
執行時
執行時已添加了對併發濫用對映的輕量級、盡力檢測。一如既往,如果一個 goroutine 正在寫入對映,則其他 goroutine 不應併發讀取或寫入對映。如果執行時檢測到此情況,它將列印診斷資訊並使程式崩潰。要了解更多關於此問題的資訊,最好的方法是在 競爭檢測器 下執行程式,它將更可靠地識別競爭並提供更多詳細資訊。
對於導致程式終止的 panic,執行時現在預設只打印正在執行的 goroutine 的堆疊,而不是所有現有 goroutine。通常只有當前 goroutine 與 panic 相關,因此省略其他 goroutine 顯著減少了崩潰訊息中無關的輸出。要在崩潰訊息中檢視所有 goroutine 的堆疊,請將環境變數 GOTRACEBACK 設定為 all 或在崩潰前呼叫 debug.SetTraceback,然後重新執行程式。有關詳細資訊,請參閱 執行時文件。
更新:旨在轉儲整個程式狀態的未捕獲 panic(例如檢測到超時或顯式處理接收到的訊號時)現在應在 panic 之前呼叫 debug.SetTraceback("all")。搜尋 signal.Notify 的用法可能有助於識別此類程式碼。
在 Windows 上,Go 1.5 及更早版本中的 Go 程式在啟動時透過呼叫 timeBeginPeriod(1) 強制將全域性 Windows 計時器解析度設定為 1ms。Go 不再需要此功能來獲得良好的排程器效能,並且更改全域性計時器解析度在某些系統上導致了問題,因此已刪除了該呼叫。
使用 -buildmode=c-archive 或 -buildmode=c-shared 構建存檔或共享庫時,訊號處理已更改。在 Go 1.5 中,存檔或共享庫會為大多數訊號安裝訊號處理程式。在 Go 1.6 中,它將只為處理 Go 程式碼中的執行時 panic 所需的同步訊號安裝訊號處理程式:SIGBUS、SIGFPE、SIGSEGV。有關詳細資訊,請參閱 os/signal 包。
反射
reflect 包已 解決了 gc 和 gccgo 工具鏈之間關於包含匯出欄位的嵌入式未匯出結構型別長期存在的不相容性。使用反射遍歷資料結構的程式碼,特別是實現 encoding/json 和 encoding/xml 包風格的序列化的程式碼,可能需要更新。
當使用反射遍歷嵌入式未匯出結構型別欄位到該結構的匯出欄位時,會出現問題。在這種情況下,reflect 曾錯誤地將嵌入式欄位報告為已匯出,透過返回空 Field.PkgPath。現在它正確地將欄位報告為未匯出,但在評估對結構中包含的匯出欄位的訪問時忽略了該事實。
更新:通常,以前遍歷結構並使用
f.PkgPath != ""
排除不可訪問欄位的程式碼現在應該使用
f.PkgPath != "" && !f.Anonymous
例如,請參閱 encoding/json 和 encoding/xml 實現的更改。
排序
在 sort 包中,Sort 的實現已重寫,使對 Interface 的 Less 和 Swap 方法的呼叫減少了大約 10%,從而節省了總體時間。新演算法對於比較相等的值(即 Less(i, j) 和 Less(j, i) 都為 false 的那些對)確實選擇了與以前不同的排序。
更新:Sort 的定義不保證相等值的最終順序,但新行為仍可能破壞預期特定順序的程式。此類程式應完善其 Less 實現以報告所需順序,或切換到 Stable,後者保留相等值的原始輸入順序。
模板
在 text/template 包中,有兩個重要的新功能可以使編寫模板更容易。
首先,現在可以 修剪模板操作周圍的空格,這可以使模板定義更具可讀性。操作開頭的減號表示修剪操作之前的空格,操作結尾的減號表示修剪操作之後的空格。例如,模板
{{23 -}}
<
{{- 45}}
格式化為 23<45。
其次,新的 {{block}} 操作,結合允許重新定義命名模板,提供了一種定義模板片段的簡單方法,這些片段可以在不同的例項化中替換。在 text/template 包中有一個 示例 演示了這一新功能。
對庫的微小更改
archive/tar包的實現糾正了檔案格式在罕見邊緣情況下的許多錯誤。一個可見的變化是,Reader型別的Read方法現在將特殊檔案型別的內容顯示為空,立即返回io.EOF。- 在
archive/zip包中,Reader型別現在有一個RegisterDecompressor方法,而Writer型別現在有一個RegisterCompressor方法,從而可以控制單個 zip 檔案的壓縮選項。這些方法優先於已有的全域性RegisterDecompressor和RegisterCompressor函式。 bufio包的Scanner型別現在有一個Buffer方法,用於指定在掃描過程中使用的初始緩衝區和最大緩衝區大小。這使得在需要時可以掃描大於MaxScanTokenSize的標記。同樣對於Scanner,該包現在定義了ErrFinalToken錯誤值,供 拆分函式 使用以中止處理或返回最終的空標記。compress/flate包已棄用其ReadError和compress/flate、compress/gzip和compress/zlib包現在對截斷的輸入流報告io.ErrUnexpectedEOF,而不是io.EOF。- 在 GCM 解密失敗的情況下,
crypto/cipher包現在會覆蓋目標緩衝區。這是為了讓 AESNI 程式碼避免使用臨時緩衝區。 crypto/tls包有一些小的改動。現在它允許當Config的Certificates為 nil 時Listen成功,只要設定了GetCertificate回撥;它增加了對 RSA 與 AES-GCM 密碼套件的支援;它還增加了一個RecordHeaderError,允許客戶端(特別是net/http包)在嘗試與非 TLS 伺服器建立 TLS 連線時報告更好的錯誤。crypto/x509包現在允許證書包含負序列號(技術上是錯誤,但實際上很常見),並且它定義了一個新的InsecureAlgorithmError,以便在拒絕使用 MD5 等不安全演算法簽名的證書時提供更好的錯誤訊息。debug/dwarf和debug/elf包共同增加了對壓縮 DWARF 段的支援。使用者程式碼無需更新:讀取時會自動解壓縮這些段。debug/elf包增加了對一般壓縮 ELF 段的支援。使用者程式碼無需更新:讀取時會自動解壓縮這些段。但是,壓縮的Sections不支援隨機訪問:它們具有 nilReaderAt欄位。encoding/asn1包現在匯出了 標籤和類常量,這些常量對於高階解析 ASN.1 結構很有用。- 同樣在
encoding/asn1包中,Unmarshal現在拒絕各種非標準的整數和長度編碼。 encoding/base64包的Decoder已修復,可以處理輸入的最後位元組。以前它儘可能多地處理四位元組標記,但忽略了剩餘的部分,最多三個位元組。因此,Decoder現在正確處理未填充編碼(如 RawURLEncoding)中的輸入,但它也拒絕填充編碼中截斷或以無效位元組(如尾隨空格)結尾的輸入。encoding/json包現在在 marshalingNumber之前檢查其語法,要求它符合 JSON 規範中的數值。與以前的版本一樣,零Number(空字串)被 marshaling 為字面量 0(零)。encoding/xml包的Marshal函式現在支援cdata屬性,例如chardata,但將其引數編碼在一個或多個<![CDATA[ ... ]]>標籤中。- 同樣在
encoding/xml包中,Decoder的Token方法在遇到所有開放標籤未關閉的 EOF 時現在會報告錯誤,這與其輸入中標籤必須正確匹配的一般要求一致。要避免該要求,請使用RawToken。 fmt包現在允許任何整數型別作為Printf的*寬度和精度規範的引數。在以前的版本中,*的引數必須是int型別。- 同樣在
fmt包中,Scanf現在可以使用 %X 掃描十六進位制字串,作為 %x 的別名。兩種格式都接受任何大小寫混合的十六進位制。 image和image/color包添加了NYCbCrA和NYCbCrA型別,以支援具有非預乘 Alpha 的 Y’CbCr 影像。io包的MultiWriter實現現在實現了WriteString方法,供WriteString使用。- 在
math/big包中,Int添加了Append和Text方法,以提供更多列印控制。 - 同樣在
math/big包中,Float現在實現了encoding.TextMarshaler和encoding.TextUnmarshaler,允許它以自然形式透過encoding/json和encoding/xml包進行序列化。 - 同樣在
math/big包中,Float的Append方法現在支援特殊的精度引數 -1。正如在strconv.ParseFloat中一樣,精度 -1 表示使用最少的位數,以便Parse將結果讀入相同精度的Float時將產生原始值。 math/rand包新增了Read函式,同樣Rand也新增了Read方法。這些使得生成偽隨機測試資料更加容易。請注意,與該包的其餘部分一樣,這些不應在加密環境中使用;為此目的,請改用crypto/rand包。net包的ParseMAC函式現在接受 20 位元組的 IP-over-InfiniBand (IPoIB) 鏈路層地址。- 同樣在
net包中,DNS 查詢有一些變化。首先,DNSError錯誤實現現在實現了Error,特別是其新的IsTemporary方法對於 DNS 伺服器錯誤返回 true。其次,DNS 查詢函式(例如LookupAddr)現在在 Plan 9 和 Windows 上返回帶根域名(帶尾隨點)的域名,以匹配 Go 在 Unix 系統上的行為。 net/http包除了已經討論過的 HTTP/2 支援之外,還有一些小的補充。首先,FileServer現在按檔名對其生成的目錄列表進行排序。其次,如果請求的 URL 路徑包含“..” (點點) 作為路徑元素,ServeFile函式現在拒絕提供結果。程式通常應該使用FileServer和Dir,而不是直接呼叫ServeFile。需要響應包含點點 URL 的請求提供檔案內容的程式仍然可以呼叫ServeContent。第三,Client現在允許使用者程式碼設定Expect:100-continue頭(參見Transport.ExpectContinueTimeout)。第四,有 五個新的錯誤程式碼:StatusPreconditionRequired(428)、StatusTooManyRequests(429)、StatusRequestHeaderFieldsTooLarge(431) 和StatusNetworkAuthenticationRequired(511) 來自 RFC 6585,以及最近批准的StatusUnavailableForLegalReasons(451)。第五,CloseNotifier的實現和文件已大幅更改。Hijacker介面現在在以前使用CloseNotifier的連線上也能正常工作。文件現在描述了CloseNotifier何時應該工作。- 同樣在
net/http包中,關於處理其Method欄位設定為空字串的Request資料結構有一些更改。空的Method欄位一直被記錄為"GET"的別名,並且仍然如此。但是,Go 1.6 修復了一些例程,這些例程沒有將空的Method與顯式的"GET"同等對待。最值得注意的是,在以前的版本中,Client僅在Method顯式設定為"GET"時才遵循重定向;在 Go 1.6 中,Client也遵循空Method的重定向。最後,NewRequest接受一個method引數,該引數未被記錄為允許為空。在過去的版本中,傳遞空的method引數會導致Request具有空的Method欄位。在 Go 1.6 中,生成的Request始終具有初始化的Method欄位:如果其引數為空字串,NewRequest會將返回的Request中的Method欄位設定為"GET"。 net/http/httptest包的ResponseRecorder現在使用與http.Server中相同的內容嗅探演算法初始化預設的 Content-Type 頭。net/url包的Parse在解析主機名方面現在更加嚴格並更符合規範。例如,主機名中的空格不再被接受。- 同樣在
net/url包中,Error型別現在實現了net.Error。 os包的IsExist、IsNotExist和IsPermission在查詢SyscallError時現在返回正確的結果。- 在類 Unix 系統上,當寫入
os.Stdout或os.Stderr(更準確地說,是為檔案描述符 1 或 2 開啟的os.File)由於管道損壞錯誤而失敗時,程式將引發SIGPIPE訊號。預設情況下,這將導致程式退出;這可以透過為syscall.SIGPIPE呼叫os/signalNotify函式來更改。對除 1 或 2 之外的檔案描述符上的損壞管道的寫入將簡單地向呼叫者返回syscall.EPIPE(可能包裝在os.PathError和/或os.SyscallError中)。在連續 10 次寫入損壞管道後引發不可捕獲的SIGPIPE訊號的舊行為不再發生。 - 在
os/exec包中,當命令以不成功的狀態退出時,Cmd的Output方法繼續返回ExitError。如果標準錯誤本來會被丟棄,則返回的ExitError現在包含失敗命令的標準錯誤輸出的字首和字尾(目前為 32 KB),用於除錯或包含在錯誤訊息中。ExitError的String方法不顯示捕獲的標準錯誤;程式必須從資料結構中單獨檢索它。 - 在 Windows 上,
path/filepath包的Join函式現在正確處理基路徑為相對驅動器路徑的情況。例如,Join(c:,a)現在返回c:a,而不是像過去版本那樣返回c:\a。這可能會影響預期不正確結果的程式碼。 - 在
regexp包中,Regexp型別始終可以安全地被併發 goroutine 使用。它使用sync.Mutex來保護在正則表示式搜尋期間使用的暫存空間快取。一些高併發伺服器在許多 goroutine 中使用相同的Regexp時,由於該互斥鎖的爭用而導致效能下降。為了幫助此類伺服器,Regexp現在有一個Copy方法,它會建立Regexp的副本,該副本共享原始結構的大部分,但擁有自己的暫存空間快取。兩個 goroutine 可以使用不同的Regexp副本而沒有互斥鎖爭用。副本確實有額外的空間開銷,因此只有在觀察到爭用時才應使用Copy。 strconv包增加了IsGraphic,類似於IsPrint。它還增加了QuoteToGraphic、QuoteRuneToGraphic、AppendQuoteToGraphic和AppendQuoteRuneToGraphic,類似於QuoteToASCII、QuoteRuneToASCII等。ASCII系列轉義除 ASCII 空格 (U+0020) 之外的所有空格字元。相反,Graphic系列不轉義任何 Unicode 空格字元(Zs 類別)。- 在
testing包中,當測試呼叫 t.Parallel 時,該測試會暫停,直到所有非並行測試完成,然後該測試繼續執行所有其他並行測試。Go 1.6 更改了此類測試的報告時間:以前的時間只計算並行執行,但現在它也計算從測試開始到呼叫t.Parallel的時間。 text/template包除了上面描述的 主要更改 之外,還包含兩個次要更改。首先,它添加了一個新的ExecError型別,用於在Execute期間發生的任何不源於對底層寫入器進行Write的錯誤。呼叫者可以透過檢查ExecError來區分模板使用錯誤和 I/O 錯誤。其次,Funcs方法現在檢查用作FuncMap中鍵的名稱是否是可以在模板函式呼叫中出現的識別符號。如果不是,Funcs會 panic。time包的Parse函式一直拒絕任何大於 31 的月份日期,例如 1 月 32 日。在 Go 1.6 中,Parse現在也拒絕非閏年的 2 月 29 日、2 月 30 日、2 月 31 日、4 月 31 日、6 月 31 日、9 月 31 日和 11 月 31 日。