Go 1.2 發行說明
Go 1.2 簡介
自 2013 年 4 月 Go 1.1 版 發布以來,發布時程已縮短,以提高發布程序的效率。這個版本,簡稱 Go 1.2,在 1.1 發行後約六個月問世,而 1.1 則是在 1.0 發布一年多後才出現。因為時程縮短,所以 1.2 的更新幅度小於 1.0 至 1.1 的差異,不過仍有一些重大的進展,包括更好的排程器和的一個新語言功能。當然,Go 1.2 仍保有相容性的承諾。絕大多數使用 Go 1.1 (或 1.0) 建構的程式,在移轉到 1.2 時無需任何變更即可執行,但說明語言中有一個角落受限,可能會曝露出原本就錯誤的程式碼 (請參閱關於使用 nil的說明)。
語言變更
為了確立規範,已澄清一個邊緣狀況,並對程式造成影響。另外有一個新的語言功能。
使用 nil
語言現已規定,為安全考量,某些使用 nil 指標的方式保證會觸發執行時間驚恐。例如,在 Go 1.0 中,給定類似這樣的程式碼
type T struct {
X [1<<24]byte
Field int32
}
func main() {
var x *T
...
}
nil
指標 x
可以用來不正確地存取記憶體:表達式 x.Field
可以存取位址 1<<24
的記憶體。為了防止這種不安全的行為,在 Go 1.2 中,編譯器現在保證透過 nil 指標的任何間接存取,例如這裡顯示的,以及 nil 到陣列的指標、nil 介面值、nil 序列等,都會驚恐或傳回正確的非 nil 安全值。簡而言之,任何明示或暗示需要評估 nil 位址的表達式都是錯誤。此實作可能會在編譯程式中注入額外的測試來強制此行為。
詳細資料請見 設計文件。
更新:大多數仰賴舊行為的程式碼都是有錯誤的,執行時會失敗。此類程式需要手動更新。
三索引序列
Go 1.2 新增了使用既有陣列或序列的切片運算時,除了長度之外還可指定容量的功能。切片運算會建立一個新序列,方法是描述已建立陣列或序列的連續區段
var array [10]int
slice := array[2:4]
序列的容量是切片可容納的最大元素數目,即使重新切片之後仍然是這樣,它反映了底層陣列的大小。在此範例中,切片
變數的容量為 8。
Go 1.2 新增新的語法,讓切片運算除了長度之外還能指定容量。第二個冒號引入了容量值,該值必須小於或等於來源序列或陣列的容量,並根據原本來調整。例如,
slice = array[2:4:7]
會將序列設定為與早期範例中相同的長度,但其容量現在只有 5 個元素 (7-2)。不可能使用這個新的序列值來存取原始陣列的最後三個元素。
此三索引標示法中,遺失的第一個索引 ([:i:j]
) 預設為零,但其他兩個索引必須永遠明確指定。Go 之後的版本有可能会引入這些索引的預設值。
詳細資料請見 設計文件。
更新:這是一個向後相容的變更,不會影響任何現有程式。
對實作和工具的變更
排程器中的搶先
在以前版本中,永無止盡迴圈的 goroutine 會導致相同執行緒的其他 goroutine 資源匱乏,特別是當 GOMAXPROCS 僅提供一個使用者執行緒時,就會是一個嚴重的問題。在 Go 1.2 中,這個問題已部分解決:排程器在進入函式時會偶爾被呼叫。這表示含有(非內聯)函式呼叫的任何迴圈都可以被搶佔,允許其他 goroutine 在相同執行緒執行。
執行緒數量限制
Go 1.2 引入了可設定限制(預設 10,000)到單一程式可以在其地址空間中擁有的總執行緒數量,以避免在某些環境中出現資源匮乏的問題。請注意,goroutine 會多工到執行緒中,所以這個限制不會直接限制 goroutine 的數量,僅限制可能同時在系統呼叫中阻塞的數量。在實務上,這個限制很難達到。
新的 SetMaxThreads
函式在 runtime/debug
套件中控制執行緒數量限制。
更新:會有少數函式受到限制的影響,但如果程式因達到限制而結束,可以修改程式以呼叫 SetMaxThreads
設定更高的數量。更好的做法是重構程式碼以減少執行緒的需求,減少核心資源的消耗。
堆疊大小
在 Go 1.2 中,建立 goroutine 時堆疊的最小大小已從 4KB 提升至 8KB。許多程式在效能重要的區段引入了昂貴的堆疊區段切換而造成效能問題。新的數字是依據經驗測試而定。
在另一個極端,新的函式 SetMaxStack
在 runtime/debug
套件中控制單一 goroutine 堆疊的最大大小。預設值在 64 位元系統中為 1GB,32 位元系統中為 250MB。在 Go 1.2 以前,失控遞迴很輕易就能用盡機器上的所有記憶體。
更新:增大的最小堆疊大小可能會導致包含許多 goroutine 的程式使用更多記憶體。這沒有解決辦法,不過後續版本計畫包含新的堆疊管理技術,應該可以更好地解決這個問題。
Cgo 和 C++
cgo
指令現在會呼叫 C++ 編譯器來建置已連結函式庫中以 C++ 撰寫的任何部分;文件說明 有更多詳細資訊。
Godoc 和 vet 已移至 go.tools 子存放庫
這兩個二進位檔案仍然包含在發行版本裡,但 godoc 和 vet 指令的原始程式碼已移至 go.tools 子存放庫中。
此外,godoc 程式已分割成 函式庫 的核心,而指令本身在獨立的 目錄 中。讓該程式碼可輕易更新,而分割成函式庫和指令也讓構建自訂二進位檔案變得更輕鬆,可供本地網站和各種部署方法使用。
更新:由於 godoc 和 vet 不是函式庫的一部分,因此用戶端 Go 程式碼不會仰賴其來源,不需要更新。
從 golang.org 取得的可執行二進位檔案分佈,包含這些二進位檔案,所以這些分佈的用戶不會受影響。
從來源構建時,用戶必須使用 「go get」來安裝 godoc 和 vet。(這些二進位檔案會持續安裝於慣用位置,而非 $GOPATH/bin
。)
$ go get code.google.com/p/go.tools/cmd/godoc
$ go get code.google.com/p/go.tools/cmd/vet
gccgo 狀態
我們預期未來的 GCC 4.9 版本會包含完全支援 Go 1.2 的 gccgo。在目前(4.8.2)的 GCC 版本中,gccgo 採用 Go 1.1.2。
gc 編譯器和連結器的變更
Go 1.2 對 gc 編譯器套件的運作方式進行了許多語意變更。大多數用戶都將不受影響。
cgo
指令現可在與其連結的函式庫中包含 C++ 時使用。有關詳細資訊,請參閱 cgo
文件。
gc 編譯器在程式沒有 package
子句時,顯示其原始設定的殘留詳細資料:它假設檔案在 package main
中。過去已消除,現在遺失 package
子句是錯誤。
在 ARM 上,工具鏈支援「外部連結」,這有助於使用 gc 工具鏈建置共用函式庫,以及為需要支援動態連結的環境提供動態連結支援。
在針對 ARM 的執行時期中,使用 5a
時,通常可使用 R9
和 R10
直接參照執行時期內部的 m
(機器)和 g
(goroutine)變數。現在需用適當的名稱來參照這些變數。
同樣也在 ARM 上,5l
連結器 (sic) 現將 MOVBS
和 MOVHS
指令定義為 MOVB
和 MOVH
的同義字,以更清楚地區分有號和無號的子字元組移動;無號版本已使用 U
字尾存在。
測試範圍
go test
的一項主要新功能是,它現在可運算,並透過已另外安裝的「go tool cover」新程式協助顯示測試範圍結果。
cover 工具是 go.tools
子儲存庫的一部分。可透過以下方法安裝:
$ go get code.google.com/p/go.tools/cmd/cover
cover 工具執行下列兩個動作。首先,當「go test」使用 -cover
旗號時,它會自動執行,以重寫套件的來源並插入工具敘述。然後,測試會照常編譯和執行,且會回報基本的範圍統計資料
$ go test -cover fmt
ok fmt 0.060s coverage: 91.4% of statements
$
其次,對於更詳盡的報告,可以設定不同的標誌以「執行測試」,來建立涵蓋範圍設定檔,然後再利用「執行工具涵蓋範圍」來進行分析。
關於如何產生及分析涵蓋範圍統計資料的詳細資訊,可以透過執行下列指令找出
$ go help testflag
$ go tool cover -help
已刪除 go doc 指令
已刪除「go doc」指令。請注意,godoc
工具本身並未刪除,只有由 go
指令包裝的部分被刪除。它原本只會根據套件路徑顯示套件的文件,而 godoc 本身已能更靈活地處理此功能。所以為了減少文件工具的數量,並作為 godoc 重組工作的一部分,改用更好的選項,因此刪除此指令。
更新:對於仍然需要在目錄中執行
$ go doc
此精確功能的人,其行為與執行
$ godoc .
對 go 指令的變更
go get
指令現在多了 -t
標誌,它會下載套件執行的測試相依性,而非套件本身的相依性。預設會和之前一樣,不下載測試的相依性。
效能
標準函式庫有很多顯著的效能改善,以下說明其中的幾個。
compress/bzip2
的解壓縮速度快了約 30%。crypto/des
套件的執行速度快了約五倍。encoding/json
套件的編碼速度快了約 30%。- 在 Windows 和 BSD 系統上的網路效能,因為在執行期間整合了網路輪詢器,因此快了約 30%,這類似於 Go 1.1 中針對 Linux 和 OS X 所做的處理。
對標準函式庫的變更
archive/tar 及 archive/zip 套件
archive/tar
和 archive/zip
套件的語意發生變更,可能會中斷現有的程式。問題是它們都提供了 os.FileInfo
介面的實作,但不符合該介面的規格。特別是,它們的 Name
函式會傳回項目的完整路徑名稱,但介面規格要求此函式僅傳回基本名稱 (最終路徑元素)。
更新:由於此行為是新增的實作且有點不透明,所以程式碼不會相依於有問題的行為。如果有程式相依於有問題的行為,則需要手動找出並修正它們。
新的編碼套件
有一個新的套件,encoding
,它定義了一組標準編碼介面,可用於建構自訂的封送器和解封送器,這些套件如 encoding/xml
、encoding/json
和 encoding/binary
。這些新的介面已用於整理標準程式庫中的一些實作。
新的介面稱為 BinaryMarshaler
、BinaryUnmarshaler
、TextMarshaler
和 TextUnmarshaler
。完整的詳細資料請見套件的 文件 和一份單獨的 設計文件。
fmt 套件
fmt
套件的格式化列印常式,例如 Printf
,現允許使用格式化規格中的索引運算式任意存取要列印的資料項目。在從引數清單中擷取引數以進行格式化時,無論是用作要格式化的值或作為寬度或規格整數,新的選擇性索引記號 [
n]
會改為擷取引數 n。n 的值是從 1 開始索引的。在進行此類索引運算後,下一個由一般處理擷取的引數將會是 n+1。
例如,一般的 Printf
呼叫
fmt.Sprintf("%c %c %c\n", 'a', 'b', 'c')
會產生字串 "a b c"
,但如果使用此類索引運算式,
fmt.Sprintf("%[3]c %[1]c %c\n", 'a', 'b', 'c')
結果會是 “"c a b"
。索引 [3]
會存取第三個格式化引數,也就是 'c'
,[1]
會存取第一個引數 'a'
,然後下一個擷取的引數會存取下一個引數,也就是 'b'
。
這項功能的目的是程式化的格式化陳述式,以便按不同的順序存取用於在地化的引數,但它也有其他用途。
log.Printf("trace: value %v of type %[1]T\n", expensiveFunction(a.b[c]))
更新:格式化規格語法的變更 строго向後相容,因此它不會影響任何運作中的程式。
text/template 和 html/template 套件
text/template
套件在 Go 1.2 中有一些變更,這些變更也反映在 html/template
套件中。
首先,針對比較基本類型的新預設函式。這些函式列於下表,表中會顯示函式名稱和相關的熟悉比較運算子。
名稱 | 運算子 | |
---|---|---|
eq | == |
|
ne | != |
|
lt | < |
|
le | <= |
|
gt | > |
|
ge | >= |
這些函數的行為與對應的 Go 函式稍微不同。首先,它們只作用於基本型別(bool
、int
、float64
、string
等)。(在某些情況下,Go 也允許陣列和結構體的比較。)其次,值的比較只要它們是同種類值即可:例如任何有符號整數值都可以與任何其他有符號整數值比較。(Go 不允許比較 int8
和 int16
)。最後,eq
函數(僅此函數)允許將第一個引數與後面一個或多個引數進行比較。本範例中的範本:
{{if eq .A 1 2 3}} equal {{else}} not equal {{end}}
如果 .A
等於 1、2 或 3 中的 任何一個,則報告「相等」。
第二個變更為語法加入一個小外掛,讓「if else if」鏈更容易寫。與其撰寫:
{{if eq .A 1}} X {{else}} {{if eq .A 2}} Y {{end}} {{end}}
可將第二個「if」摺疊到「else」中,並只有一個「end」,如下所示:
{{if eq .A 1}} X {{else if eq .A 2}} Y {{end}}
兩種形式的作用相同;這其中的差異只在於語法。
更新:「else if」變更和比較函數都不會影響現有的程式。那些已透過函數對應定義稱為 eq
等函數的則不受影響,因為相關函數對應將會覆寫新的預設函數定義。
新套件
新增了兩個套件。
encoding
套件上方說明過。image/color/palette
套件提供標準顏色調色盤。
函式庫的次要變更
以下清單總計許多函式庫的次要變更,大部分為外掛。請見相關套件文件,以取得每個變更的更多資訊。
archive/zip
套件加入DataOffset
存取器,以傳回儲存檔案內(可能是壓縮的)資料在儲存檔內的偏移量。bufio
套件加入Reset
方法到Reader
和Writer
。這些方法允許Reader
和Writer
在新的輸入和輸出 reader 和 writer 上重新使用,從而節省配置時間。compress/bzip2
現在可以解壓縮串接的儲存檔。compress/flate
套件在Writer
上加入Reset
方法,從而在建立包含多個壓縮檔的儲存檔時,能夠減少配置時間。compress/gzip
套件的Writer
型別加入Reset
,讓它可以重複使用。compress/zlib
套件的Writer
類型新增Reset
,因此可以重複使用。container/heap
套件新增Fix
方法,以提供更有效率的方法來更新項目在堆積中的位置。container/list
套件新增MoveBefore
和MoveAfter
方法,用來實作明顯的重新排列。crypto/cipher
套件新增新的 GCM 模式(Galois 計數模式),幾乎總是與 AES 加密一起使用。crypto/md5
套件新增新的Sum
函數,以簡化雜湊計算,同時不犧牲效能。- 類似地,
crypto/sha1
套件新增新的Sum
函數。 - 此外,
crypto/sha256
套件新增Sum256
和Sum224
函數。 - 最後,
crypto/sha512
套件新增Sum512
和Sum384
函數。 crypto/x509
套件新增支援來讀取和寫入任意擴充。crypto/tls
套件新增支援 TLS 1.1、1.2 和 AES-GCM。database/sql
套件新增SetMaxOpenConns
方法於DB
,以限制與資料庫開啟的連線數。encoding/csv
套件現在總是允許欄位使用尾隨逗號。encoding/gob
套件現在將結構的通道和函數欄位處理為未匯出的,即使它們不是未匯出的。也就是說,它完全忽略這些欄位。以前這些欄位會觸發錯誤,如果內嵌結構新增此類欄位,可能會導致意外的相容性問題。此套件現在也支援上述encoding
套件的一般性BinaryMarshaler
與BinaryUnmarshaler
介面。encoding/json
套件現在在列印字串時,總是將與號跳脫為「\u0026」。它現在會接受無效的 UTF-8 於Marshal
(以前會拒絕此類輸入),但會予以修正。最後,它現在支援上述encoding
套件的一般性編碼介面。encoding/xml
套件現可讓儲存在指標的屬性進行編製。它也藉由新的Marshaler
、Unmarshaler
,以及相關MarshalerAttr
與UnmarshalerAttr
介面來支援上方所述encoding
套件的通用編製介面。這個套件也新增了一個Flush
方法到Encoder
類型,供使用者自訂編碼器使用。請參閱EncodeToken
的文件,了解如何使用它。flag
套件現在有一個Getter
介面,可以用來取得旗標的值。由於 Go 1 相容性指南,這個方法無法新增到現有的Value
介面,但所有現有的標準旗標類型都實作了它。這個套件現在也匯出了CommandLine
旗標組,它包含命令列的旗標。go/ast
套件的SliceExpr
結構有一個新的布林值欄位,Slice3
,當表示一個有三個索引的切片表示式(兩個冒號)時,它會設為 true。預設值為 false,表示通常的二索引型式。go/build
套件將AllTags
欄位新增到Package
類型中,以利處理建置標籤。image/draw
套件現在匯出了一個介面,Drawer
,它封裝標準Draw
方法。Porter-Duff 運算子現在實作這個介面,實際上是將一個運算綁定到繪圖運算子,而不是明確提供它。給定一個調色板影像作為目標,Drawer
介面的新FloydSteinberg
實作會使用 Floyd-Steinberg 誤差擴散演算法來繪製影像。要建立適合這種處理的調色板,新的Quantizer
介面代表量化演算法的實作,這些演算法會根據全彩色的影像選擇一個調色板。程式庫中沒有這個介面的實作。image/gif
套件現在可以使用新的Encode
和EncodeAll
函式建立 GIF 檔。它們的選項引數允許指定要使用的影像Quantizer
;如果它為nil
,產生的 GIF 將會使用新的image/color/palette
套件定義的新Plan9
色彩映射 (色板)。這些選項也會指定要使用哪個Drawer
來建立輸出影像;如果它為nil
,將使用 Floyd-Steinberg 錯誤擴散。io
套件的Copy
方法現在會以不同的方式優先處理它的引數。如果一個引數實作WriterTo
而另一個實作ReaderFrom
,Copy
現在會呼叫WriterTo
來執行工作,以便通常需要較少的中間緩衝。net
套件預設需要 cgo,因為主機作業系統通常必須調解網路呼叫設定。然而,有些系統可以在不使用 cgo 的情況下使用網路,這對避免動態連結很有用。新的建構標籤netgo
(預設為關閉)允許在能夠這樣做的系統上,以純 Go 的方式建構net
套件。net
套件新增一個新欄位DualStack
至Dialer
結構,用於使用雙重 IP 堆疊如 RFC 6555 描述中所述設定 TCP 連線。net/http
套件不再傳輸根據 RFC 6265 有誤的 cookie。它只會記錄錯誤且不傳送任何東西。此外,net/http
套件的ReadResponse
函式現在允許*Request
參數為nil
,此時它會假設是 GET 要求。最後,HTTP 伺服器現在會透明地提供 HEAD 要求,處理器程式碼中不需要特別情況。在提供 HEAD 要求時,寫入至Handler
的ResponseWriter
會被Server
吸收,而且客戶端會收到 HTTP 規格規範的空主體。os/exec
套件的Cmd.StdinPipe
方法傳回一個io.WriteCloser
,但已經從 *os.File 更改其實現為一個嵌入 *os.File 的非外傳型別,且現在安全地關閉傳回的值。在 Go 1.2 之前,有一個無法避免的競爭情況,這個變更修復了這個情況。需要存取到 *os.File 方法的程式碼可以使用介面類型聲明,例如wc.(interface{ Sync() error })
。runtime
套件放寬了在 SetFinalizer` 中的完成函式的約束;實際上,引數現在可以是任何可指定給該函式正式型別的型別,就像 Go 中的任何函式呼叫一樣。sort
套件有一個新的Stable
函式,可以實現穩定的排序。但其效率低於正常的排序演算法。strings
套件新增了一個IndexByte
函式,以與bytes
套件相符。sync/atomic
套件新增了一組新的交換函式,這些函式會將引數與儲存在指標中的值進行原子交換,並傳回舊值。這些函式包括SwapInt32
、SwapInt64
、SwapUint32
、SwapUint64
、SwapUintptr
以及SwapPointer
(用於交換unsafe.Pointer
)。syscall
套件現在已為 Darwin 實作Sendfile
。testing
套件現在會匯出TB
介面。它會記錄T
和B
型別中常見的方法,以簡化在測試和基準測試之間共用程式碼。此外,AllocsPerRun
函式現在會將傳回值量化為整數(儘管它的型別仍然是float64
),以捨去因初始化而產生的任何誤差,並讓結果更具可重複性。text/template
套件現在會在評量「escape」函式的引數時,自動解除指標值的參照,例如「html」,以使其行為與其他列印函式(例如「printf」)一致。- 在
time
套件中,Parse
函式和Format
方法現在會處理带有秒數的時間區調整,例如在歷史日期「1871-01-01T05:33:02+00:34:08」中。此外,對這些常式進行樣式比對更加嚴格:非小寫字母現在必須接在「Jan」和「Mon」等標準字詞之後。 unicode
套件新增了In
,它比原來的IsOneOf
更容易使用,但功能相同,可以查看某個字元是否屬於 Unicode 類別。