Go 1.12 發行說明

Go 1.12 簡介

最新版的 Go,版本 1.12,於 Go 1.11 的六個月後推出。大多數的變更在於工具鏈、執行時期和函式庫的實作。與以往一樣,此次發行維持 Go 1 相容性承諾。我們預期幾乎所有 Go 程式都能繼續編譯和執行,就像過去一樣。

變更

語言規格並未變更。

移植

競爭偵測器現在支援 linux/arm64

Go 1.12 是最後一個支援 FreeBSD 10.x 的發行版本,該版本已達到生命週期終點。Go 1.13 將需要 FreeBSD 11.2+ 或 FreeBSD 12.0+。FreeBSD 12.0+ 需要具有 COMPAT_FREEBSD11 選項的內核(這是預設值)。

cgo 現在支援 linux/ppc64

hurd 現在是 GOOS 的公認值,專門用於 GNU/Hurd 系統,搭配 gccgo 使用。

Windows

Go 的新 windows/arm 移植支援在 32 位元的 ARM 晶片上執行 Go 的 Windows 10 IoT Core,例如 Raspberry Pi 3。

AIX

Go 現可在 POWER8 架構(aix/ppc64)中支援 AIX 7.2 以上版本。然而,外部連結、cgo、pprof 和競賽偵測仍不支援。

Darwin

Go 1.12 是最後一個可在 macOS 10.10 Yosemite 上執行的版本。Go 1.13 將需要 macOS 10.11 El Capitan 或更高版本。

現在在 Darwin 上執行的系統呼叫會使用 libSystem,確保與未來版本的 macOS 和 iOS 相容。轉移至 libSystem 會觸發 App Store 針對私人 API 使用情況進行額外檢查。由於它被視為私人使用,syscall.Getdirentries 現在會在 iOS 上永遠傳回 ENOSYS。此外,syscall.Setrlimit 會在過去成功的地方回報錯誤的 argument。這些後果與 Go 無關,而且使用者應預期往後與 libSystem 的實作具有行為相等性。

工具

go tool vet 不再支援

go vet 指令已被改寫為各種不同原始碼分析工具的基礎。請參閱 golang.org/x/tools/go/analysis 套件以取得詳情。這造成副作用是,go tool vet 不再受到支援。使用 go tool vet 的外部工具必須變更為使用 go vet。在所有支援版本的 Go 中,使用 go vet 取代 go tool vet 應該也能適用。

做為這個變更的一部分,實驗性的 -shadow 選項不再可以在 go vet 中使用。現在可以使用下列方式檢查變數隱藏

go get -u golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)

巡覽

Go 巡覽不再包含在主二進位分發中。若要手動安裝,請執行下列步驟,而不是執行 go tool tour

go get -u golang.org/x/tour
tour

建立緩存需求

現在要求使用 建立快取,其目的為逐步淘汰 $GOPATH/pkg。設定環境變數 GOCACHE=off 將造成需要寫入快取的 go 指令會執行失敗。

純二進位套件

Go 1.12 是最後一個將支援純二進位套件的版本。

Cgo

Go 1.12 將會把 C 類型 EGLDisplay 轉換為 Go 類型 uintptr。這個變更與 Go 1.10 及更新版本處理 Darwin 的 CoreFoundation 和 Java 的 JNI 類型的方式類似。請參閱 cgo 文件 以取得更多資訊。

使用 Cgo 的套件不再接受扭曲的 C 名稱。請改用 Cgo 名稱。例如,請使用記錄的 cgo 名稱 C.char,而不是 cgo 所產生的扭曲名稱 _Ctype_char

模組

如果將 GO111MODULE 設為 on,現在 go 指令支援模組目錄外及模組感知操作,前提是這些操作不需要解析相對於目前目錄的匯入路徑,或明確編輯 go.mod 檔案。go getgo listgo mod download 等指令行為就像是在最初為空的需求下在模組中。在此模式中,go env GOMOD 會報告系統的空裝置(/dev/nullNUL)。

現在可以安全地同時執行下載和擷取模組的 go 指令。模組快取 (GOPATH/pkg/mod) 必須置於支援檔案鎖定的檔案系統。

go.mod 檔案中的 go 指向現在表示那個模組內檔案所使用的語言版本。如果沒有現有版本,它會設為目前的版本 (go 1.12)。如果模組的 go 指向指定較 新的 版本的工具鏈,go 指令會嘗試不論如何建立封裝,且只有建立失敗時才會註記不匹配。

如此更改 go 指向的使用方式表示,如果您使用 Go 1.12 來建立模組,在 go.mod 檔案中記錄 go 1.12,嘗試使用 Go 1.11 至 Go 1.11.3 來建立相同模組時,您就會收到錯誤。Go 1.11.4 或更新版本可以正常執行,早於 Go 1.11 的版本也是。如果您必須使用 Go 1.11 至 1.11.3,您可以透過 go mod edit -go=1.11 使用 Go 1.12 go 工具將語言版本設為 1.11 來避免這個問題。

如果無法使用 active 模組來解析匯入,go 指令現在會嘗試使用主模組的 replace 指向中提到的模組,然後再查閱模組快取和平常的網路來源。如果找不到符合的取代,但 replace 指向沒有指定版本,go 指令會使用衍生自零 time.Time(例如 v0.0.0-00010101000000-000000000000)的擬似版本。

編譯器工具鏈

編譯器的動態變數分析已經改善。這表示執行程式在本次版本中可能會比前一個版本更早執行結束處理常式。如果這是個問題,請考慮適當加入 runtime.KeepAlive 呼叫。

現在可以更彈性地對更多函式套用程式內嵌機制,包括只呼叫另一個函式的函式。這個額外的程式內嵌機制讓使用 runtime.CallersFrames 而不是直接反覆執行 runtime.Callers 的結果變得特別重要。

// Old code which no longer works correctly (it will miss inlined call frames).
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
for _, pc := range pcs[:n] {
    f := runtime.FuncForPC(pc)
    if f != nil {
        fmt.Println(f.Name())
    }
}
// New code which will work correctly.
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
for {
    frame, more := frames.Next()
    fmt.Println(frame.Function)
    if !more {
        break
    }
}

編譯器用於實現方法表達式的封裝程序不再由 runtime.CallersFramesruntime.Stack 報告。它們也不會列印在恐慌堆疊追蹤中。此變更將 gc 工具鏈調整為與 gccgo 工具鏈相符,後者已經在堆疊追蹤中省略此類封裝程序。這些 API 的客戶端可能需要針對遺失的框架進行調整。對於必須在 1.11 和 1.12 版之間進行互操作的程式碼,您可以使用函式字面 func (...) { x.M(...) } 替換方法表達式 x.M

編譯器現在接受 -lang 旗標,用於設定要使用的 Go 語言版本。例如,-lang=go1.8 會讓編譯器在程式使用類型別名(於 Go 1.9 新增)時產生錯誤。在 Go 1.12 之前所做的語言變更並未持續強制執行。

編譯器工具鏈現在使用不同的慣例來呼叫 Go 函式和組譯函式。這對於使用者來說應該沒有影響,除了同時跨越 Go 和組譯 *且* 跨越封裝邊界的呼叫之外。如果連結產生像「relocation target not defined for ABIInternal (but is defined for ABI0)」這樣的錯誤,請參閱 ABI 設計文件中的 相容性區段

編譯器產生的 DWARF 除錯資訊已獲得許多改進,包括參數列印和變數位置資訊的改進。

Go 程式現在也維護 linux/arm64 上的堆疊幀指標,讓像 perf 之類的剖析工具受益。幀指標維護有很小的執行時間開銷,開銷會有所不同,但平均約為 3%。若要建構不使用幀指標的工具鏈,請在執行 make.bash 時設定 GOEXPERIMENT=noframepointer

已經移除過時的「安全」編譯器模式(透過 -u gcflag 啟用)。

godocgo doc

在 Go 1.12 中,godoc 不再具有命令列介面,而且只是一個網站伺服器。使用者應使用 go doc 代替,來輸出命令列說明。Go 1.12 是包含 godoc 網站伺服器的最後一個版本;在 Go 1.13 中,它將透過 go get 取得。

現在 go doc 支援 -all 旗標,這會讓它列印所有匯出的 API 及其文件,就像 godoc 命令列過去執行的操作一樣。

go doc 現在也包含 -src 旗標,它將顯示目標的原始程式碼。

追蹤

追蹤工具現在支援繪製突變器的使用率曲線,包括執行追蹤的交叉參照。它們有助於分析垃圾收集器對應用程式延遲和通量帶來的影響。

組譯器

arm64 上,平台暫存器已從 R18 重新命名為 R18PLATFORM 以防止意外使用,因為作業系統可能會選擇保留該暫存器。

執行期間

Go 1.12大幅改善了垃圾回收後大量堆積資料仍舊存在的掃描效能。這會降低在垃圾回收後立即進行資源配置的延遲時間。

Go 執行期間現在會更積極地將記憶體釋放回作業系統,特別針對無法重新使用現有堆積空間的大型資源配置。

Go 執行期間的計時器及截止時間程式碼執行得更快且可以擴充至使用更多 CPU。特別的是,這改善了網路連線截止時程的操作效能。

在 Linux 上,執行期間現在會使用 MADV_FREE 來釋放未使用的記憶體。這比較有效率,但可能會導致報告的 RSS 更高。當系統需要時核心會取出未使用的資料。若要改回 Go 1.11 的行為 (MADV_DONTNEED),請設定環境變數 GODEBUG=madvdontneed=1

cpu.extension=off 新增到 GODEBUG 環境變數中現在會停用在標準程式庫和執行期間中使用選用的 CPU 指令集延伸。這尚未在 Windows 上支援。

Go 1.12 增加了記憶體檔案的準確性,方法是修正大型堆疊資源配置的過度計算問題。

追蹤、runtime.Callerruntime.Callers 不再包含編譯器產生的初始化函式。在全域變數初始化期間執行追蹤現在會顯示名為 PKG.init.ializers 的函式。

標準函式庫

TLS 1.3

根據 RFC 8446 的規定,Go 1.12 於 crypto/tls 套件中新增了 TLS 1.3 的選擇性支援。若要啟用,請將值 tls13=1 新增至 GODEBUG 環境變數。Go 1.13 將預設啟用此功能。

若要協商 TLS 1.3,請務必不要在 Config 中設定明確的 MaxVersion,並使用已設定環境變數 GODEBUG=tls13=1 的程式執行您的程式。

除了 ConnectionState 中的 TLSUnique 和重新協商之外,TLS 1.2 的所有功能在 TLS 1.3 中都可用,而且提供等同或更好的安全性與效能。請注意,雖然 TLS 1.3 向下相容於先前版本,但某些舊系統在嘗試協商此版本時可能無法正常運作。大小過小而無法確保安全的 RSA 憑證金鑰 (包括 512 位元金鑰) 將無法與 TLS 1.3 搭配使用。

TLS 1.3 密碼組件不可設定。所有支援的密碼組件都很安全,而如果在 Config 中設定 PreferServerCipherSuites,就偏好順序基於可用的硬體。

早期資料(也稱為「0-RTT 模式」)目前不支援做為用戶端或伺服器。此外,Go 1.12 伺服器不支援略過客戶端傳送的意外早期資料。由於 TLS 1.3 0-RTT 模式牽涉到客戶端保留狀態,以表示支援 0-RTT 的伺服器,因此,Go 1.12 伺服器無法成為負載平衡池的一部分,而部分其他伺服器確實支援 0-RTT。如果將一個網域從支援 0-RTT 的伺服器切換到 Go 1.12 伺服器,則必須在切換之前停用 0-RTT 至少在已發布的通行證期限期間,才能確保不中斷作業。

在 TLS 1.3 中,客戶端會在握手結束時最後說話,因此如果它在伺服器上造成錯誤,會在客戶端的第一次 Read 而非 Handshake 回傳。例如,如果伺服器拒絕客戶端憑證,就會發生這種情況。類似地,通行證現在是後握手訊息,所以是僅在客戶端第一次 Read 時接收。

函式庫的次要變更

一如往常,函式庫有各種次要變更和更新,並考量 Go 1 相容性承諾

bufio

ReaderUnreadRuneUnreadByte 方法現在會傳回錯誤,如果它們在 Peek 之後被呼叫。

bytes

新的函式 ReplaceAll 會傳回一個位元組切片的拷貝,其中所有值的不相交重複部分都已更換為其他值。

指向零值 Reader 的指標現在在功能上相當於 NewReader(nil)。在 Go 1.12 之前,前者不能在所有情況下都做為後者的替換。

crypto/rand

現在會在第一次 Reader.Read 因為等待從核心讀取熵而阻擋超過 60 秒時,印出警告到標準錯誤訊息中。

在 FreeBSD 上,如果可用,Reader 現在會使用 getrandom 系統呼叫,否則會使用 /dev/urandom

crypto/rc4

此發行版本會移除組譯實作,只留下純 Go 版本。Go 編譯器會產生稍好或稍差的程式碼,這取決於精確的 CPU。RC4 不安全,且應該只用於相容於舊系統。

crypto/tls

如果客戶端傳送一個看起來不像 TLS 的最初訊息,伺服器將不再會回覆警示,並在 RecordHeaderError 新欄位 Conn 中揭露底層的net.Conn

database/sql

現在可以透過傳遞 *Rows 值給 Row.Scan 方式取得查詢游標。

expvar

新的 Delete 方法允許從 Map 刪除金鑰/值對。

fmt

現在會依照金鑰排序來列印地圖以利於測試。下列為排序規則

在列印地圖時,先前會將非反身的金鑰值(如 NaN)顯示為 <nil>。在此版本中,將列印正確的值。

go/doc

為了解決 cmd/doc 中的一些未解決問題,此套件有新的 Mode 位元,PreserveAST,可用於控制是否清除 AST 資料。

go/token

File 類型有一個新的 LineStart 欄位,用於傳回特定行開頭的位置。這對於偶爾處理非 Go 檔案(例如組譯)但想使用 token.Pos 機制識別檔案位置的程式特別有用。

image

RegisterFormat 函式現已能安全地並行使用。

image/png

顏色少於 16 色的調色板影像現在會編碼成更小的輸出檔案。

io

新的 StringWriter 介面包裝著 WriteString 函式。

math

函式 SinCosTanSincos 現在會對巨大的參數套用 Payne-Hanek 範圍縮減法。這會產生更精確的答案,但它們不會與早期版本的結果完全相同。

math/bits

新的進位精確運算 AddSubMulDiv 的版本有 uintuint32uint64 三種。

net

現已忽略並棄用 Dialer.DualStack 設定;RFC 6555 快速回退 (“Happy Eyeballs”) 現已預設啟用。若要停用,請將 Dialer.FallbackDelay 設定為負值。

同樣地,若 Dialer.KeepAlive 為零,現已預設啟用 TCP 保持存活功能。若要停用,請將其設定為負值。

在 Linux 上,現已在從 UnixConn 複製到 TCPConn 時使用 splice 系統呼叫

net/http

HTTP 伺服器現在會拒絕以純文字「400 錯誤要求」回應寄送至 HTTPS 伺服器的錯誤 HTTP 要求。

新的 Client.CloseIdleConnections 方法會呼叫 Client 底層 TransportCloseIdleConnections(若有)。

Transport 不再拒絕宣告 HTTP 尾端資訊但未使用區塊編碼的 HTTP 回應。現在僅會忽略已宣告的尾端資訊。

Transport 不再如在 Go 1.10 和 Go 1.11 中那麼嚴格地處理由 HTTP/2 伺服器宣告的 MAX_CONCURRENT_STREAMS 值。預設行為現在已回復到 Go 1.9 中的行為:每個與伺服器的連線最多可以有 MAX_CONCURRENT_STREAMS 個要求處於啟用狀態,然後視需要建立新的 TCP 連線。在 Go 1.10 和 Go 1.11 中,http2 套件會封鎖並等待要求完成,而不是建立新的連線。若要回復更嚴格的行為,請直接匯入 golang.org/x/net/http2 套件,並將 Transport.StrictMaxConcurrentStreams 設定為 true

net/url

ParseParseRequestURIURL.Parse 現在會針對包含 ASCII 控制字元的 URL (包括 NULL、tab 和換行符號) 傳回錯誤。

net/http/httputil

ReverseProxy 現在會自動代理 WebSocket 要求。

os

新的 ProcessState.ExitCode 方法會傳回程序的結束代碼。

已將 ModeCharDevice 加入 ModeType 位元遮罩,讓在使用 ModeType 遮罩 FileMode 時,可以復原 ModeDevice | ModeCharDevice

新的函式 UserHomeDir 會傳回目前使用者的家目錄。

RemoveAll 現在在大部分的 Unix 系統上支援長度超過 4096 個字元的路徑。

File.Sync 如今在 macOS 上使用 F_FULLFSYNC 將檔案內容正確刷新至永久儲存. 此舉可能會導致方法執行速度比前次版本慢一點。

File 如今支援 SyscallConn 方法,它會回傳 syscall.RawConn 介面值. 此介面可用來呼叫基礎檔案描述符中的特定系統操作。

path/filepath

當傳入 Windows 上的保留檔名(例如 NUL)時,IsAbs 函式如今會傳回 true。 保留名稱清單。

reflect

新的 MapIter 類型是資料範圍的迭代器. 此類型透過 Value 類型的 MapRange 方法公開. 它遵循和範圍語句相同的迭代語意,用 Next 推進迭代器,用 Key/Value 存取各個條目。

regexp

Copy 不再需要用來避免鎖定競爭,因此已給予部分不推荐使用的註解. Copy 可能仍適用於使用它的原因是製作兩個具有不同 Longest 設定的副本。

runtime/debug

新的 BuildInfo 類型公開從執行中的二進檔讀取的組建資訊,只在以模組支援組建的二進檔中可用. 這包括主套件路徑、主模組資訊和模組相依關係. 此類型透過 ReadBuildInfo 函式在 BuildInfo 上給予。

strings

新的函式 ReplaceAll 會回傳一份字串副本,其中所有非重疊的特定值都已替換成其他值。

指向零值 Reader 的指標現在在功能上等同於 NewReader(nil). 在 Go 1.12 之前,前者無法在所有情況下用作後者的替換。

新的 Builder.Cap 方法會回傳組建器底層位元組切片的容量。

字元對應函數 MapTitleToLowerToLowerSpecialToTitleToTitleSpecialToUpperToUpperSpecial 現在保證永遠傳回有效的 UTF-8。在早期版本中,如果輸入為無效的 UTF-8 但不需套用任何字元替換,這些常式就會錯誤地將無效的 UTF-8 傳回且未經修改。

syscall

現在支援 FreeBSD 12 上的 64 位元 inode。有些類型已做相應調整。

相容版本的 Windows 現在支援 Unix socket (AF_UNIX) 位址系列。

新的函數 Syscall18 已於 Windows 中推出,允許呼叫具有最多 18 個引數。

syscall/js

Callback 類型和 NewCallback 函數已重新命名;它們現在分別稱為 FuncFuncOf。這是一種重大變更,但 WebAssembly 支援仍處於實驗階段,尚未受 Go 1 相容性保證 約束。任何使用舊名稱的程式碼都必須更新。

如果類型實作新的 Wrapper 介面,ValueOf 會使用它來傳回該類型的 JavaScript 值。

Value 的意義已變更。它現在代表 JavaScript undefined 值,而非數字零。這是一種重大變更,但 WebAssembly 支援仍處於實驗階段,尚未受 Go 1 相容性保證 約束。任何依賴零 Value 表示數字零的程式碼都必須更新。

新的 Value.Truthy 方法會報告給定值的 JavaScript “真偽性”

testing

-benchtime 旗標現在支援設定明確的執行計數,而不是在值以 x 結尾時設定時間。例如,-benchtime=100x 會執行基準測試 100 次。

text/template

在執行範本時,長內容值不再會在錯誤中被截斷。

於 <<.very.deep.context.v...> 執行「tmpl」時:映射沒有「notpresent」鍵的項目

現在變為

於 <<.very.deep.context.value.notpresent> 執行「tmpl」時:映射沒有「notpresent」鍵的項目

如果範本呼叫時發生自定義函式的程序錯誤,現下這個錯誤會被 ExecuteExecuteTemplate 方法偵測到並傳回為一個錯誤。

時間

$GOROOT/lib/time/zoneinfo.zip 中的時間資料庫已更新為 2018i 版本。請注意,只有當作業系統未提供時,才會使用此 ZIP 檔案。

不安全

將 nil 的 unsafe.Pointer 轉換成 uintptr 並使用算術運算還原是不正確的。(這種情形原本就是不正確的,但現在會導致編譯器產生異常的狀況。)