Go 1 發行說明
Go 1 簡介
Go 版本 1,簡稱 Go 1,定義了一種語言和一套核心庫,為建立可靠的產品、專案和出版物提供了穩定的基礎。
Go 1 的主要動機是為使用者提供穩定性。人們應該能夠編寫 Go 程式,並期望它們在數年內(包括在 Google App Engine 等生產環境中)無需更改即可繼續編譯和執行。同樣,人們應該能夠編寫關於 Go 的書籍,能夠說明書籍描述的是哪個 Go 版本,並且該版本號在很久以後仍然有意義。
在 Go 1 中編譯的程式碼,除了少數例外,在 Go 1 版本的整個生命週期內應繼續編譯和執行,即使我們釋出了諸如 Go 版本 1.1、1.2 等更新和錯誤修復。除了關鍵修復外,Go 1 後續版本對語言和庫所做的更改可能會增加功能,但不會破壞現有的 Go 1 程式。Go 1 相容性文件更詳細地解釋了相容性準則。
Go 1 代表了 Go 當前的使用方式,而不是對語言的徹底重新思考。我們避免設計新功能,而是專注於清理問題和不一致之處,並提高可移植性。我們曾考慮並原型化了一些 Go 語言和包的更改,但主要因為它們重大且向後不相容而未釋出。Go 1 提供了一個機會來發布它們,這對於長期發展是有益的,但這也意味著 Go 1 引入了與舊程式的不相容性。幸運的是,go fix 工具可以自動完成將程序升級到 Go 1 標準所需的大部分工作。
本文件概述了 Go 1 中影響更新現有程式碼的程式設計師的主要更改;其參考點是之前的版本 r60(標記為 r60.3)。它還解釋瞭如何更新 r60 程式碼以在 Go 1 下執行。
語言變化
Append
append 預宣告的可變引數函式使得透過在切片末尾新增元素來增長切片變得容易。常見的用法是在生成輸出時向位元組切片末尾新增位元組。然而,append 沒有提供將字串追加到 []byte 的方法,這是另一個常見情況。
greeting := []byte{}
greeting = append(greeting, []byte("hello ")...)
類比 copy 的類似屬性,Go 1 允許將字串(按位元組)直接追加到位元組切片,減少了字串和位元組切片之間的摩擦。不再需要轉換。
greeting = append(greeting, "world"...)
更新:這是一個新功能,因此現有程式碼無需更改。
Close
close 預宣告函式提供了一種機制,供傳送方發出不再發送值的訊號。它對通道上的 for range 迴圈的實現很重要,並且在其他情況下也很有用。部分是出於設計,部分是由於可能發生的競爭條件,它旨在僅供在通道上傳送的 goroutine 使用,而不是供接收資料的 goroutine 使用。然而,在 Go 1 之前,沒有編譯時檢查 close 是否正確使用。
為了彌補這一缺陷,至少部分地,Go 1 禁止在只接收通道上使用 close。嘗試關閉此類通道是編譯時錯誤。
var c chan int
var csend chan<- int = c
var crecv <-chan int = c
close(c) // legal
close(csend) // legal
close(crecv) // illegal
更新:嘗試關閉只接收通道的現有程式碼即使在 Go 1 之前也是錯誤的,應該修復。編譯器現在將拒絕此類程式碼。
複合字面量
在 Go 1 中,如果陣列、切片或對映型別的複合字面量的元素初始化器是指標型別,則可以省略型別規範。本示例中的所有四個初始化都是合法的;最後一個在 Go 1 之前是非法的。
type Date struct {
month string
day int
}
// Struct values, fully qualified; always legal.
holiday1 := []Date{
Date{"Feb", 14},
Date{"Nov", 11},
Date{"Dec", 25},
}
// Struct values, type name elided; always legal.
holiday2 := []Date{
{"Feb", 14},
{"Nov", 11},
{"Dec", 25},
}
// Pointers, fully qualified, always legal.
holiday3 := []*Date{
&Date{"Feb", 14},
&Date{"Nov", 11},
&Date{"Dec", 25},
}
// Pointers, type name elided; legal in Go 1.
holiday4 := []*Date{
{"Feb", 14},
{"Nov", 11},
{"Dec", 25},
}
更新:此更改對現有程式碼沒有影響,但是對現有原始碼應用 gofmt -s 命令,除其他外,將在允許的情況下省略顯式元素型別。
init 期間的 Goroutine
舊語言規定在初始化期間執行的 go 語句建立 goroutine,但它們直到整個程式初始化完成才開始執行。這在許多地方引入了笨拙,並實際上限制了 init 構造的實用性:如果另一個包可以在初始化期間使用庫,則庫被迫避免使用 goroutine。這種設計是為了簡單和安全,但是,隨著我們對語言的信心增長,它似乎沒有必要。在初始化期間執行 goroutine 與在正常執行期間執行它們沒有更復雜或更不安全。
在 Go 1 中,使用 goroutine 的程式碼可以從 init 例程和全域性初始化表示式中呼叫,而不會引入死鎖。
var PackageGlobal int
func init() {
c := make(chan int)
go initializationFunction(c)
PackageGlobal = <-c
}
更新:這是一個新功能,因此現有程式碼無需更改,儘管依賴於 goroutine 在 main 之前不啟動的程式碼可能會中斷。標準儲存庫中沒有這樣的程式碼。
rune 型別
語言規範允許 int 型別為 32 或 64 位寬,但當前的實現將 int 設定為 32 位,即使在 64 位平臺上也是如此。最好在 64 位平臺上將 int 設定為 64 位。(這對大型切片的索引有重要影響。)然而,如果 int 從 32 位增長到 64 位,這將浪費舊語言處理 Unicode 字元時的空間,因為 int 型別也用於儲存 Unicode 碼點:每個碼點將浪費額外的 32 位儲存空間。
為了使更改為 64 位 int 可行,Go 1 引入了一個新的基本型別 rune,用於表示單個 Unicode 碼點。它是 int32 的別名,類似於 byte 作為 uint8 的別名。
字元字面量,例如 'a'、'語' 和 '\u0345',現在預設型別為 rune,類似於 1.0 預設型別為 float64。因此,除非另有說明,否則初始化為字元常量的變數將具有 rune 型別。
庫已更新,在適當的時候使用 rune 而不是 int。例如,函式 unicode.ToLower 及其相關函式現在接受並返回 rune。
delta := 'δ' // delta has type rune.
var DELTA rune
DELTA = unicode.ToUpper(delta)
epsilon := unicode.ToLower(DELTA + 1)
if epsilon != 'δ'+1 {
log.Fatal("inconsistent casing for Greek")
}
更新:大多數原始碼不會受到影響,因為 := 初始化器的型別推斷會默默地引入新型別,並從那裡傳播。某些程式碼可能會出現型別錯誤,可以透過簡單的轉換來解決。
error 型別
Go 1 引入了一個新的內建型別 error,其定義如下:
type error interface {
Error() string
}
由於此型別的所有影響都在包庫中,因此將在下面討論。
從對映中刪除
在舊語言中,要從對映 m 中刪除鍵為 k 的條目,需要編寫以下語句:
m[k] = value, false
這種語法是一個特殊的特例,是唯一的二對一賦值。它需要傳遞一個被求值但丟棄的值(通常被忽略),以及一個幾乎總是常量 false 的布林值。它完成了任務,但很奇怪且有爭議。
在 Go 1 中,該語法已消失;取而代之的是一個新的內建函式 delete。呼叫:
delete(m, k)
將刪除由表示式 m[k] 檢索到的對映條目。沒有返回值。刪除不存在的條目是空操作。
更新:執行 go fix 將把形式為 m[k] = value, false 的表示式轉換為 delete(m, k),當明確忽略的值可以安全地從程式中丟棄,並且 false 指的是預定義的布林常量時。fix 工具將標記語法在其他用途,供程式設計師檢查。
在對映中迭代
舊語言規範沒有定義對映的迭代順序,實際上在不同硬體平臺之間存在差異。這導致迭代對映的測試不穩定且不可移植,具有令人不快的特性,即測試可能在一臺機器上總是透過,但在另一臺機器上卻會中斷。
在 Go 1 中,當使用 for range 語句迭代對映時,元素的訪問順序被定義為不可預測的,即使相同的迴圈在相同的對映上執行多次也是如此。程式碼不應假定元素以任何特定順序訪問。
此更改意味著依賴迭代順序的程式碼很可能在成為問題之前就早期中斷並得到修復。同樣重要的是,它允許對映實現即使在程式使用 range 迴圈從對映中選擇元素時也能確保更好的對映平衡。
m := map[string]int{"Sunday": 0, "Monday": 1}
for name, value := range m {
// This loop should not assume Sunday will be visited first.
f(name, value)
}
更新:這是工具無法提供幫助的一項更改。大多數現有程式碼不會受到影響,但有些程式可能會中斷或出現異常行為;我們建議手動檢查所有對映上的 range 語句,以驗證它們不依賴迭代順序。標準儲存庫中存在一些此類示例;它們已得到修復。請注意,依賴迭代順序(未指定)已經是錯誤的。此更改將不可預測性編入規範。
多重賦值
語言規範長期以來一直保證在賦值中,所有右側表示式都在任何左側表示式賦值之前進行求值。為了保證可預測的行為,Go 1 進一步完善了規範。
如果賦值語句的左側包含需要求值的表示式,例如函式呼叫或陣列索引操作,則這些都將使用通常的從左到右規則完成,然後才將值賦給任何變數。一旦所有內容都求值完畢,實際的賦值將按從左到右的順序進行。
這些示例說明了此行為。
sa := []int{1, 2, 3}
i := 0
i, sa[i] = 1, 2 // sets i = 1, sa[0] = 2
sb := []int{1, 2, 3}
j := 0
sb[j], j = 2, 1 // sets sb[0] = 2, j = 1
sc := []int{1, 2, 3}
sc[0], sc[0] = 1, 2 // sets sc[0] = 1, then sc[0] = 2 (so sc[0] = 2 at end)
更新:這是工具無法提供幫助的一項更改,但發生中斷的可能性很小。標準儲存庫中沒有程式碼因此更改而中斷,並且依賴於先前未指定行為的程式碼已經是錯誤的。
返回值和影子變數
一個常見的錯誤是在對與結果變數同名但不是同一個變數的變數賦值後使用 return(不帶引數)。這種情況稱為影子:結果變數被內部作用域中宣告的另一個同名變數所遮蔽。
在具有命名返回值的函式中,Go 1 編譯器不允許在返回語句處任何命名返回值被遮蔽的情況下使用不帶引數的 return 語句。(這不是規範的一部分,因為這是我們仍在探索的一個領域;情況類似於編譯器拒絕不以顯式 return 語句結束的函式。)
此函式隱式返回一個被遮蔽的返回值,將被編譯器拒絕。
func Bug() (i, j, k int) {
for i = 0; i < 5; i++ {
for j := 0; j < 5; j++ { // Redeclares j.
k += i * j
if k > 100 {
return // Rejected: j is shadowed here.
}
}
}
return // OK: j is not shadowed here.
}
更新:以這種方式遮蔽返回值的程式碼將被編譯器拒絕,需要手動修復。標準儲存庫中出現的少數情況大多是錯誤。
複製包含未匯出欄位的結構體
舊語言不允許一個包複製包含屬於不同包的未匯出欄位的結構體值。但是,方法接收器有一個必需的例外;此外,copy 和 append 的實現從未遵守此限制。
Go 1 將允許包複製包含來自其他包的未匯出欄位的結構體值。除了解決不一致性之外,此更改還引入了一種新的 API:一個包可以返回不透明值,而無需訴諸指標或介面。time.Time 和 reflect.Value 的新實現是利用此新屬性的型別示例。
例如,如果包 p 包含以下定義:
type Struct struct {
Public int
secret int
}
func NewStruct(a int) Struct { // Note: not a pointer.
return Struct{a, f(a)}
}
func (s Struct) String() string {
return fmt.Sprintf("{%d (secret %d)}", s.Public, s.secret)
}
匯入 p 的包可以隨意賦值和複製 p.Struct 型別的值。在幕後,未匯出欄位將被賦值和複製,就像它們是匯出的一樣,但客戶端程式碼永遠不會知道它們。程式碼:
import "p"
myStruct := p.NewStruct(23)
copyOfMyStruct := myStruct
fmt.Println(myStruct, copyOfMyStruct)
將顯示結構體的秘密欄位已複製到新值。
更新:這是一個新功能,因此現有程式碼無需更改。
相等性
在 Go 1 之前,該語言沒有定義結構體和陣列值的相等性。這意味著,除其他外,結構體和陣列不能用作對映鍵。另一方面,Go 定義了函式和對映值的相等性。函式相等性在閉包存在的情況下存在問題(兩個閉包何時相等?),而對映相等性比較的是指標,而不是對映的內容,這通常不是使用者想要的。
Go 1 解決了這些問題。首先,結構體和陣列可以進行相等性和不等性比較(== 和 !=),因此可以用作對映鍵,前提是它們由也定義了相等性的元素組成,使用逐元素比較。
type Day struct {
long string
short string
}
Christmas := Day{"Christmas", "XMas"}
Thanksgiving := Day{"Thanksgiving", "Turkey"}
holiday := map[Day]bool{
Christmas: true,
Thanksgiving: true,
}
fmt.Printf("Christmas is a holiday: %t\n", holiday[Christmas])
其次,Go 1 除了與 nil 比較之外,取消了函式值的相等性定義。最後,對映相等性也消失了,同樣除了與 nil 比較之外。
請注意,切片的相等性仍然未定義,因為計算通常不可行。另請注意,結構體和陣列的有序比較運算子(< < = > >=)仍然未定義。
更新:結構體和陣列相等性是一個新功能,因此現有程式碼無需更改。依賴函式或對映相等性的現有程式碼將被編譯器拒絕,需要手動修復。受影響的程式很少,但修復可能需要一些重新設計。
包層次結構
Go 1 解決了舊標準庫中的許多缺陷,並清理了許多包,使它們更具內部一致性和可移植性。
本節描述了 Go 1 中包的重新排列方式。有些已移動,有些已重新命名,有些已刪除。新包將在後續章節中描述。
包層次結構
Go 1 具有重新排列的包層次結構,將相關項分組到子目錄中。例如,utf8 和 utf16 現在位於 unicode 的子目錄中。此外,一些包已移至 code.google.com/p/go 的子儲存庫,而其他包已被徹底刪除。
| 舊路徑 | 新路徑 |
|---|---|
| asn1 | encoding/asn1 |
| csv | encoding/csv |
| gob | encoding/gob |
| json | encoding/json |
| xml | encoding/xml |
| exp/template/html | html/template |
| big | math/big |
| cmath | math/cmplx |
| rand | math/rand |
| http | net/http |
| http/cgi | net/http/cgi |
| http/fcgi | net/http/fcgi |
| http/httptest | net/http/httptest |
| http/pprof | net/http/pprof |
| net/mail | |
| rpc | net/rpc |
| rpc/jsonrpc | net/rpc/jsonrpc |
| smtp | net/smtp |
| url | net/url |
| exec | os/exec |
| scanner | text/scanner |
| tabwriter | text/tabwriter |
| template | text/template |
| template/parse | text/template/parse |
| utf8 | unicode/utf8 |
| utf16 | unicode/utf16 |
請注意,舊的 cmath 和 exp/template/html 包的包名已更改為 cmplx 和 template。
更新:執行 go fix 將更新所有仍在標準儲存庫中的包的匯入和包重新命名。匯入不再在標準儲存庫中的包的程式需要手動編輯。
exp 包樹
由於它們不是標準化的,exp 目錄下的包將不會在標準的 Go 1 發行版中提供,儘管它們將以原始碼形式在儲存庫中提供給希望使用它們的開發人員。
Go 1 釋出時,有幾個包已移至 exp 目錄下:
ebnfhtml†go/types
(†EscapeString 和 UnescapeString 型別仍保留在 html 包中。)
所有這些包都以相同的名稱提供,字首為 exp/:exp/ebnf 等。
此外,utf8.String 型別已移至其自己的包 exp/utf8string。
最後,gotype 命令現在位於 exp/gotype 中,而 ebnflint 現在位於 exp/ebnflint 中。如果它們已安裝,則現在位於 $GOROOT/bin/tool 中。
更新:使用 exp 中包的程式碼需要手動更新,或者從具有可用 exp 的安裝中編譯。go fix 工具或編譯器將抱怨此類使用。
舊包樹
由於它們已被棄用,old 目錄下的包將不會在標準的 Go 1 發行版中提供,儘管它們將以原始碼形式提供給希望使用它們的開發人員。
這些包的新位置是:
old/netchan
更新:使用現在位於 old 中的包的程式碼需要手動更新,或者從具有可用 old 的安裝中編譯。go fix 工具將警告此類使用。
已刪除的包
Go 1 徹底刪除了幾個包:
container/vectorexp/datafmtgo/typecheckerold/regexpold/templatetry
以及命令 gotry。
更新:使用 container/vector 的程式碼應更新為直接使用切片。請參閱Go 語言社群 Wiki 獲取一些建議。使用其他包的程式碼(應該幾乎沒有)需要重新考慮。
遷移到子儲存庫的包
Go 1 已將許多包移到其他儲存庫中,通常是主要 Go 儲存庫的子儲存庫。此表列出了舊的和新的匯入路徑:
| 舊路徑 | 新路徑 |
|---|---|
| crypto/bcrypt | code.google.com/p/go.crypto/bcrypt |
| crypto/blowfish | code.google.com/p/go.crypto/blowfish |
| crypto/cast5 | code.google.com/p/go.crypto/cast5 |
| crypto/md4 | code.google.com/p/go.crypto/md4 |
| crypto/ocsp | code.google.com/p/go.crypto/ocsp |
| crypto/openpgp | code.google.com/p/go.crypto/openpgp |
| crypto/openpgp/armor | code.google.com/p/go.crypto/openpgp/armor |
| crypto/openpgp/elgamal | code.google.com/p/go.crypto/openpgp/elgamal |
| crypto/openpgp/errors | code.google.com/p/go.crypto/openpgp/errors |
| crypto/openpgp/packet | code.google.com/p/go.crypto/openpgp/packet |
| crypto/openpgp/s2k | code.google.com/p/go.crypto/openpgp/s2k |
| crypto/ripemd160 | code.google.com/p/go.crypto/ripemd160 |
| crypto/twofish | code.google.com/p/go.crypto/twofish |
| crypto/xtea | code.google.com/p/go.crypto/xtea |
| exp/ssh | code.google.com/p/go.crypto/ssh |
| image/bmp | code.google.com/p/go.image/bmp |
| image/tiff | code.google.com/p/go.image/tiff |
| net/dict | code.google.com/p/go.net/dict |
| net/websocket | code.google.com/p/go.net/websocket |
| exp/spdy | code.google.com/p/go.net/spdy |
| encoding/git85 | code.google.com/p/go.codereview/git85 |
| patch | code.google.com/p/go.codereview/patch |
| exp/wingui | code.google.com/p/gowingui |
更新:執行 go fix 將更新這些包的匯入以使用新的匯入路徑。依賴這些包的安裝需要使用 go get 命令來安裝它們。
庫的主要更改
本節描述核心庫的重大更改,這些更改影響了大多數程式。
錯誤型別和 errors 包
os.Error 放在 os 包中主要是歷史原因:錯誤最初是在實現 os 包時出現的,當時它們看起來與系統相關。從那時起,很明顯錯誤比作業系統更基礎。例如,在 os 依賴的包(如 syscall)中使用 Errors 會很好。此外,將 Error 放在 os 中會引入許多對 os 的依賴,否則這些依賴將不存在。
Go 1 透過引入一個內建的 error 介面型別和一個單獨的 errors 包(類似於 bytes 和 strings)來解決這些問題,該包包含實用函式。它用 errors.New 替換了 os.NewError,使錯誤在環境中佔據了更重要的位置。
因此,為了避免廣泛使用的 String 方法導致意外滿足 error 介面,error 介面改為使用 Error 作為該方法的名稱。
type error interface {
Error() string
}
fmt 庫會自動呼叫 Error,就像它已經為 String 所做的那樣,以便輕鬆列印錯誤值。
type SyntaxError struct {
File string
Line int
Message string
}
func (se *SyntaxError) Error() string {
return fmt.Sprintf("%s:%d: %s", se.File, se.Line, se.Message)
}
所有標準包都已更新為使用新介面;舊的 os.Error 已消失。
一個新包,errors,包含函式:
func New(text string) error
將字串轉換為錯誤。它替換了舊的 os.NewError。
var ErrSyntax = errors.New("syntax error")
更新:執行 go fix 將更新受此更改影響的幾乎所有程式碼。使用 String 方法定義錯誤型別的程式碼需要手動更新,將方法重新命名為 Error。
系統呼叫錯誤
舊的 syscall 包,它早於 os.Error(以及幾乎所有其他東西),將錯誤作為 int 值返回。反過來,os 包轉發了許多這些錯誤,例如 EINVAL,但每個平臺使用不同的錯誤集。這種行為令人不快且不可移植。
在 Go 1 中,syscall 包改為返回 error 用於系統呼叫錯誤。在 Unix 上,實現由 syscall.Errno 型別完成,該型別滿足 error 並替換了舊的 os.Errno。
影響 os.EINVAL 及其相關內容的更改在其他地方描述。
更新:執行 go fix 將更新受此更改影響的幾乎所有程式碼。無論如何,大多數程式碼應該使用 os 包而不是 syscall,因此不會受到影響。
時間
在程式語言中,很好地支援時間總是一個挑戰。舊的 Go time 包具有 int64 單位,沒有真正的型別安全,也沒有區分絕對時間和持續時間。
因此,Go 1 庫中最徹底的更改之一是對 time 包的徹底重新設計。不再將納秒數作為 int64,以及單獨的 *time.Time 型別來處理小時和年等人為單位,現在有兩種基本型別:time.Time(一個值,所以 * 消失了),它表示一個時間點;和 time.Duration,它表示一個時間間隔。兩者都具有納秒解析度。Time 可以表示遠古過去和遙遠未來的任何時間,而 Duration 只能跨越大約 290 年的正負。這些型別上都有方法,還有許多有用的預定義常量持續時間,例如 time.Second。
新方法包括 Time.Add(將 Duration 新增到 Time)和 Time.Sub(減去兩個 Time 以產生 Duration)等。
最重要的語義更改是,Unix 紀元(1970 年 1 月 1 日)現在僅與那些提及 Unix 的函式和方法相關:time.Unix 以及 Time 型別的 Unix 和 UnixNano 方法。特別是,time.Now 返回一個 time.Time 值,而不是舊 API 中的自 Unix 紀元以來的整數納秒計數。
// sleepUntil sleeps until the specified time. It returns immediately if it's too late. func sleepUntil(wakeup time.Time) { now := time.Now() // A Time. if !wakeup.After(now) { return } delta := wakeup.Sub(now) // A Duration. fmt.Printf("Sleeping for %.3fs\n", delta.Seconds()) time.Sleep(delta) }
新型別、方法和常量已透過所有使用時間的標準包(例如 os 及其檔案時間戳表示)傳播。
更新:go fix 工具將更新許多舊 time 包的使用以使用新型別和方法,儘管它不替換諸如 1e9 之類表示每秒納秒的值。此外,由於某些值發生型別更改,fix 工具重寫的一些表示式可能需要進一步手動編輯;在這種情況下,重寫將包括舊功能的正確函式或方法,但可能型別錯誤或需要進一步分析。
對庫的微小更改
本節描述較小的更改,例如那些對不常用包的更改,或者除了執行 go fix 之外隻影響少數程式的更改。此類別包括 Go 1 中新增的包。它們共同改進了可移植性,規範了行為,並使介面更加現代化和 Go 風格。
archive/zip 包
在 Go 1 中,*zip.Writer 不再具有 Write 方法。它的存在是一個錯誤。
更新:受影響的少量程式碼將被編譯器捕獲,並且必須手動更新。
bufio 包
在 Go 1 中,bufio.NewReaderSize 和 bufio.NewWriterSize 函式不再因無效大小而返回錯誤。如果引數大小太小或無效,它將被調整。
更新:執行 go fix 將更新將錯誤分配給 _ 的呼叫。未修復的呼叫將被編譯器捕獲,並且必須手動更新。
compress/flate、compress/gzip 和 compress/zlib 包
在 Go 1 中,compress/flate、compress/gzip 和 compress/zlib 中的 NewWriterXxx 函式如果接受壓縮級別,則都返回 (*Writer, error),否則返回 *Writer。gzip 包的 Compressor 和 Decompressor 型別已重新命名為 Writer 和 Reader。flate 包的 WrongValueError 型別已刪除。
更新:執行 go fix 將更新舊名稱和將錯誤分配給 _ 的呼叫。未修復的呼叫將被編譯器捕獲,並且必須手動更新。
crypto/aes 和 crypto/des 包
在 Go 1 中,Reset 方法已被刪除。Go 不保證記憶體不會被複制,因此此方法具有誤導性。
特定於密碼的型別 *aes.Cipher、*des.Cipher 和 *des.TripleDESCipher 已被刪除,取而代之的是 cipher.Block。
更新:刪除對 Reset 的呼叫。將特定密碼型別的使用替換為 cipher.Block。
crypto/elliptic 包
在 Go 1 中,elliptic.Curve 已被設計為介面,以允許替代實現。曲線引數已移至 elliptic.CurveParams 結構。
更新:*elliptic.Curve 的現有使用者需要更改為簡單的 elliptic.Curve。對 Marshal、Unmarshal 和 GenerateKey 的呼叫現在是 crypto/elliptic 中的函式,它們將 elliptic.Curve 作為第一個引數。
crypto/hmac 包
在 Go 1 中,雜湊特定函式(例如 hmac.NewMD5)已從 crypto/hmac 中刪除。相反,hmac.New 接受一個返回 hash.Hash 的函式,例如 md5.New。
更新:執行 go fix 將執行所需的更改。
crypto/x509 包
在 Go 1 中,crypto/x509 中的 CreateCertificate 函式和 CreateCRL 方法已更改為接受 interface{},而它們以前接受 *rsa.PublicKey 或 *rsa.PrivateKey。這將允許將來實現其他公鑰演算法。
更新:無需更改。
encoding/binary 包
在 Go 1 中,binary.TotalSize 函式已被 Size 替換,該函式接受 interface{} 引數而不是 reflect.Value。
更新:受影響的少量程式碼將被編譯器捕獲,並且必須手動更新。
encoding/xml 包
在 Go 1 中,xml 包的設計已更接近於其他編組包,例如 encoding/gob。
舊的 Parser 型別已重新命名為 Decoder 並具有新的 Decode 方法。還引入了 Encoder 型別。
函式 Marshal 和 Unmarshal 現在與 []byte 值一起工作。要使用流,請使用新的 Encoder 和 Decoder 型別。
在編組或解組值時,欄位標籤中支援的標誌格式已更改,更接近於 json 包(`xml:"name,flag"`)。欄位標籤、欄位名稱以及 XML 屬性和元素名稱之間的匹配現在區分大小寫。XMLName 欄位標籤(如果存在)也必須與要編組的 XML 元素的名稱匹配。
更新:執行 go fix 將更新包的大多數用法,除了對 Unmarshal 的一些呼叫。必須特別注意欄位標籤,因為 fix 工具不會更新它們,如果手動不修復,在某些情況下它們會默默地出現錯誤行為。例如,舊的 "attr" 現在寫為 ",attr",而純粹的 "attr" 仍然有效但含義不同。
expvar 包
在 Go 1 中,RemoveAll 函式已被刪除。Iter 函式和 *Map 上的 Iter 方法已被 Do 和 (*Map).Do 替換。
更新:大多數使用 expvar 的程式碼無需更改。使用 Iter 的罕見程式碼可以更新為將閉包傳遞給 Do 以實現相同的效果。
flag 包
在 Go 1 中,flag.Value 介面略有更改。Set 方法現在返回一個 error 而不是 bool 來指示成功或失敗。
還有一種新型的標誌,Duration,用於支援指定時間間隔的引數值。此類標誌的值必須給定單位,就像 time.Duration 格式化它們一樣:10s、1h30m 等。
var timeout = flag.Duration("timeout", 30*time.Second, "how long to wait for completion")
更新:實現自己標誌的程式需要進行小的手動修復以更新其 Set 方法。Duration 標誌是新的,不影響任何現有程式碼。
go/* 包
go 下的幾個包的 API 略有修改。
在 go/scanner、go/parser、go/printer 和 go/doc 包中引入了一個具體的 Mode 型別,用於配置模式標誌。
AllowIllegalChars 和 InsertSemis 模式已從 go/scanner 包中刪除。它們主要用於掃描除 Go 原始檔以外的文字。相反,text/scanner 包應為此目的使用。
提供給掃描器 Init 方法的 ErrorHandler 現在只是一個函式,而不是一個介面。ErrorVector 型別已被刪除,取而代之的是(現有的)ErrorList 型別,並且 ErrorVector 方法已遷移。客戶端不再將 ErrorVector 嵌入到掃描器中,現在客戶端應該維護一個 ErrorList。
go/parser 包提供的解析函式集已減少到主要的解析函式 ParseFile,以及幾個便利函式 ParseDir 和 ParseExpr。
go/printer 包支援附加的配置模式 SourcePos;如果設定,印表機將發出 //line 註釋,以便生成的輸出包含原始原始碼位置資訊。新型別 CommentedNode 可用於提供與任意 ast.Node 關聯的註釋(到目前為止,只有 ast.File 攜帶註釋資訊)。
go/doc 包的型別名稱透過刪除 Doc 字尾進行了簡化:PackageDoc 現在是 Package,ValueDoc 是 Value 等。此外,所有型別現在都一致地具有 Name 欄位(或 Names,在 Value 型別的情況下)和 Type.Factories 已變為 Type.Funcs。現在,建立一個包的文件不再呼叫 doc.NewPackageDoc(pkg, importpath),而是使用:
doc.New(pkg, importpath, mode)
其中新的 mode 引數指定操作模式:如果設定為 AllDecls,則考慮所有宣告(而不僅僅是匯出的)。函式 NewFileDoc 已刪除,函式 CommentText 已成為 ast.CommentGroup 的方法 Text。
在 go/token 包中,token.FileSet 方法 Files(最初返回一個 *token.File 通道)已被迭代器 Iterate 替換,該迭代器接受一個函式引數。
在 go/build 包中,API 幾乎完全被替換。該包仍然計算 Go 包資訊,但它不再執行構建:Cmd 和 Script 型別已消失。(要構建程式碼,請改用新的 go 命令。)DirInfo 型別現在名為 Package。FindTree 和 ScanDir 被 Import 和 ImportDir 替換。
更新:使用 go 中包的程式碼必須手動更新;編譯器將拒絕不正確的用法。與任何 go/doc 型別一起使用的模板可能需要手動修復;重新命名的欄位將導致執行時錯誤。
hash 包
在 Go 1 中,hash.Hash 的定義包含一個新方法 BlockSize。這個新方法主要用於加密庫。
hash.Hash 介面的 Sum 方法現在接受一個 []byte 引數,雜湊值將追加到該引數中。可以透過向呼叫新增 nil 引數來重新建立以前的行為。
更新:hash.Hash 的現有實現需要新增 BlockSize 方法。逐位元組處理輸入的雜湊可以實現 BlockSize 以返回 1。執行 go fix 將更新對 hash.Hash 的各種實現的 Sum 方法的呼叫。
更新:由於該包的功能是新的,因此無需更新。
http 包
在 Go 1 中,http 包進行了重構,將一些實用程式放入 httputil 子目錄中。這些部分很少被 HTTP 客戶端需要。受影響的專案是:
- ClientConn
- DumpRequest
- DumpRequestOut
- DumpResponse
- NewChunkedReader
- NewChunkedWriter
- NewClientConn
- NewProxyClientConn
- NewServerConn
- NewSingleHostReverseProxy
- ReverseProxy
- ServerConn
Request.RawURL 欄位已被刪除;它是一個歷史遺留物。
Handle 和 HandleFunc 函式,以及 ServeMux 中同名的方法,現在如果嘗試兩次註冊相同的模式,則會 panic。
更新:執行 go fix 將更新受影響的少數程式,除了 RawURL 的使用,這必須手動修復。
image 包
image 包進行了一些小的更改、重新排列和重新命名。
大多數顏色處理程式碼已移至其自己的包 image/color。對於移動的元素,出現了對稱性;例如,image.RGBA 的每個畫素都是 color.RGBA。
舊的 image/ycbcr 包已合併到 image 和 image/color 包中,並進行了一些重新命名。
舊的 image.ColorImage 型別仍在 image 包中,但已重新命名為 image.Uniform,而 image.Tiled 已被刪除。
此表列出了重新命名。
| 舊路徑 | 新路徑 |
|---|---|
| image.Color | color.Color |
| image.ColorModel | color.Model |
| image.ColorModelFunc | color.ModelFunc |
| image.PalettedColorModel | color.Palette |
| image.RGBAColor | color.RGBA |
| image.RGBA64Color | color.RGBA64 |
| image.NRGBAColor | color.NRGBA |
| image.NRGBA64Color | color.NRGBA64 |
| image.AlphaColor | color.Alpha |
| image.Alpha16Color | color.Alpha16 |
| image.GrayColor | color.Gray |
| image.Gray16Color | color.Gray16 |
| image.RGBAColorModel | color.RGBAModel |
| image.RGBA64ColorModel | color.RGBA64Model |
| image.NRGBAColorModel | color.NRGBAModel |
| image.NRGBA64ColorModel | color.NRGBA64Model |
| image.AlphaColorModel | color.AlphaModel |
| image.Alpha16ColorModel | color.Alpha16Model |
| image.GrayColorModel | color.GrayModel |
| image.Gray16ColorModel | color.Gray16Model |
| ycbcr.RGBToYCbCr | color.RGBToYCbCr |
| ycbcr.YCbCrToRGB | color.YCbCrToRGB |
| ycbcr.YCbCrColorModel | color.YCbCrModel |
| ycbcr.YCbCrColor | color.YCbCr |
| ycbcr.YCbCr | image.YCbCr |
| ycbcr.SubsampleRatio444 | image.YCbCrSubsampleRatio444 |
| ycbcr.SubsampleRatio422 | image.YCbCrSubsampleRatio422 |
| ycbcr.SubsampleRatio420 | image.YCbCrSubsampleRatio420 |
| image.ColorImage | image.Uniform |
image 包的 New 函式(NewRGBA、NewRGBA64 等)將 image.Rectangle 作為引數,而不是四個整數。
最後,有新的預定義 color.Color 變數 color.Black、color.White、color.Opaque 和 color.Transparent。
更新:執行 go fix 將更新受此更改影響的幾乎所有程式碼。
log/syslog 包
在 Go 1 中,syslog.NewLogger 函式返回一個錯誤以及一個 log.Logger。
更新:受影響的少量程式碼將被編譯器捕獲,並且必須手動更新。
mime 包
在 Go 1 中,mime 包的 FormatMediaType 函式已簡化,使其與 ParseMediaType 保持一致。它現在接受 "text/html" 而不是 "text" 和 "html"。
更新:受影響的少量程式碼將被編譯器捕獲,並且必須手動更新。
net 包
在 Go 1 中,各種 SetTimeout、SetReadTimeout 和 SetWriteTimeout 方法已被 SetDeadline、SetReadDeadline 和 SetWriteDeadline 替換。新方法不是接受一個納秒超時值應用於連線上的任何活動,而是設定一個絕對截止時間(作為 time.Time 值),在此之後讀寫將超時並且不再阻塞。
還有新的函式 net.DialTimeout,用於簡化網路地址撥號超時,以及 net.ListenMulticastUDP,允許多播 UDP 在多個偵聽器之間併發偵聽。net.ListenMulticastUDP 函式替換了舊的 JoinGroup 和 LeaveGroup 方法。
更新:使用舊方法的程式碼將無法編譯,並且必須手動更新。語義更改使得 fix 工具難以自動更新。
os 包
Time 函式已被刪除;呼叫者應使用 time 包中的 Time 型別。
Exec 函式已被刪除;呼叫者應使用 syscall 包中的 Exec(如果可用)。
ShellExpand 函式已重新命名為 ExpandEnv。
NewFile 函式現在接受 uintptr fd,而不是 int。檔案上的 Fd 方法現在也返回 uintptr。
os 包中不再有諸如 EINVAL 這樣的錯誤常量,因為值集隨底層作業系統而異。有新的可移植函式,如 IsPermission 來測試常見的錯誤屬性,以及一些具有更 Go 風格名稱的新錯誤值,例如 ErrPermission 和 ErrNotExist。
Getenverror 函式已被刪除。要區分不存在的環境變數和空字串,請使用 os.Environ 或 syscall.Getenv。
Process.Wait 方法已取消其選項引數,並且相關常量已從包中刪除。此外,函式 Wait 已消失;只有 Process 型別的方法仍然存在。
Process.Wait 返回的 Waitmsg 型別已替換為更可移植的 ProcessState 型別,該型別具有訪問器方法以恢復有關程序的資訊。由於 Wait 的更改,ProcessState 值始終描述一個已退出的程序。可移植性問題以其他方式簡化了介面,但 ProcessState.Sys 和 ProcessState.SysUsage 方法返回的值可以型別斷言為底層系統特定資料結構,例如 Unix 上的 syscall.WaitStatus 和 syscall.Rusage。
更新:執行 go fix 將刪除 Process.Wait 的零引數。所有其他更改將被編譯器捕獲,並且必須手動更新。
os.FileInfo 型別
Go 1 重新定義了 os.FileInfo 型別,將其從結構體更改為介面:
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes
Mode() FileMode // file mode bits
ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}
檔案模式資訊已移至名為 os.FileMode 的子型別,這是一種簡單的整數型別,具有 IsDir、Perm 和 String 方法。
檔案模式和屬性(例如 Unix 上的 i-number)的系統特定詳細資訊已從 FileInfo 中完全刪除。相反,每個作業系統的 os 包都提供了 FileInfo 介面的實現,該介面有一個 Sys 方法,返回檔案元資料的系統特定表示。例如,要查詢 Unix 系統上檔案的 i-number,請像這樣解包 FileInfo:
fi, err := os.Stat("hello.go")
if err != nil {
log.Fatal(err)
}
// Check that it's a Unix file.
unixStat, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
log.Fatal("hello.go: not a Unix file")
}
fmt.Printf("file i-number: %d\n", unixStat.Ino)
假設(這是不明智的)"hello.go" 是一個 Unix 檔案,i-number 表示式可以縮寫為:
fi.Sys().(*syscall.Stat_t).Ino
FileInfo 的絕大多數用途只需要標準介面的方法。
os 包不再包含 POSIX 錯誤(例如 ENOENT)的包裝器。對於少數需要驗證特定錯誤條件的程式,現在有布林函式 IsExist、IsNotExist 和 IsPermission。
f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if os.IsExist(err) {
log.Printf("%s already exists", name)
}
更新:執行 go fix 將更新使用當前 os.FileInfo 和 os.FileMode API 的舊程式碼。需要系統特定檔案詳細資訊的程式碼需要手動更新。使用 os 包中舊 POSIX 錯誤值的程式碼將無法編譯,並且也需要手動更新。
os/signal 包
Go 1 中的 os/signal 包將 Incoming 函式(該函式返回一個接收所有傳入訊號的通道)替換為選擇性的 Notify 函式,後者請求在現有通道上傳遞特定訊號。
更新:程式碼必須手動更新。字面翻譯:
c := signal.Incoming()
是:
c := make(chan os.Signal, 1)
signal.Notify(c) // ask for all signals
但大多數程式碼應該列出它想要處理的特定訊號:
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT)
path/filepath 包
在 Go 1 中,path/filepath 包的 Walk 函式已更改為接受 WalkFunc 型別的函式值,而不是 Visitor 介面值。WalkFunc 統一了檔案和目錄的處理。
type WalkFunc func(path string, info os.FileInfo, err error) error
即使無法開啟檔案或目錄,也會呼叫 WalkFunc 函式;在這種情況下,錯誤引數將描述失敗。如果目錄的內容要跳過,函式應返回 filepath.SkipDir 值。
markFn := func(path string, info os.FileInfo, err error) error {
if path == "pictures" { // Will skip walking of directory pictures and its contents.
return filepath.SkipDir
}
if err != nil {
return err
}
log.Println(path)
return nil
}
err := filepath.Walk(".", markFn)
if err != nil {
log.Fatal(err)
}
更新:此更改簡化了大多數程式碼,但具有微妙的後果,因此受影響的程式需要手動更新。編譯器將捕獲使用舊介面的程式碼。
regexp 包
regexp 包已重寫。它具有相同的介面,但它支援的正則表示式規範已從舊的“egrep”形式更改為 RE2 的形式。
更新:使用該包的程式碼應手動檢查其正則表示式。
runtime 包
在 Go 1 中,runtime 包匯出的許多 API 已被刪除,取而代之的是其他包提供的功能。使用 runtime.Type 介面或其特定具體型別實現的程式碼現在應使用 reflect 包。使用 runtime.Semacquire 或 runtime.Semrelease 的程式碼應使用通道或 sync 包中的抽象。runtime.Alloc、runtime.Free 和 runtime.Lookup 函式(一個用於除錯記憶體分配器的不安全 API)沒有替代品。
以前,runtime.MemStats 是一個全域性變數,儲存記憶體分配的統計資訊,並且呼叫 runtime.UpdateMemStats 可以確保它是最新的。在 Go 1 中,runtime.MemStats 是一個結構體型別,程式碼應該使用 runtime.ReadMemStats 來獲取當前統計資訊。
該包添加了一個新函式 runtime.NumCPU,它返回作業系統核心報告的可用於並行執行的 CPU 數量。其值可以用於設定 GOMAXPROCS。runtime.Cgocalls 和 runtime.Goroutines 函式已重新命名為 runtime.NumCgoCall 和 runtime.NumGoroutine。
更新:執行 go fix 將更新函式重新命名的程式碼。其他程式碼需要手動更新。
strconv 包
在 Go 1 中,strconv 包經過了顯著的重寫,使其更具 Go 風格,而不再是 C 風格,儘管 Atoi 仍然存在(它類似於 int(ParseInt(x, 10, 0))),Itoa(x) 也是如此(FormatInt(int64(x), 10))。還有一些函式的新變體,它們將資料追加到位元組切片而不是返回字串,以允許控制分配。
此表總結了重新命名;有關完整詳細資訊,請參閱包文件。
| 舊呼叫 | 新呼叫 |
|---|---|
| Atob(x) | ParseBool(x) |
| Atof32(x) | ParseFloat(x, 32)§ |
| Atof64(x) | ParseFloat(x, 64) |
| AtofN(x, n) | ParseFloat(x, n) |
| Atoi(x) | Atoi(x) |
| Atoi(x) | ParseInt(x, 10, 0)§ |
| Atoi64(x) | ParseInt(x, 10, 64) |
| Atoui(x) | ParseUint(x, 10, 0)§ |
| Atoui64(x) | ParseUint(x, 10, 64) |
| Btoi64(x, b) | ParseInt(x, b, 64) |
| Btoui64(x, b) | ParseUint(x, b, 64) |
| Btoa(x) | FormatBool(x) |
| Ftoa32(x, f, p) | FormatFloat(float64(x), f, p, 32) |
| Ftoa64(x, f, p) | FormatFloat(x, f, p, 64) |
| FtoaN(x, f, p, n) | FormatFloat(x, f, p, n) |
| Itoa(x) | Itoa(x) |
| Itoa(x) | FormatInt(int64(x), 10) |
| Itoa64(x) | FormatInt(x, 10) |
| Itob(x, b) | FormatInt(int64(x), b) |
| Itob64(x, b) | FormatInt(x, b) |
| Uitoa(x) | FormatUint(uint64(x), 10) |
| Uitoa64(x) | FormatUint(x, 10) |
| Uitob(x, b) | FormatUint(uint64(x), b) |
| Uitob64(x, b) | FormatUint(x, b) |
更新:執行 go fix 將更新受此更改影響的幾乎所有程式碼。
§ Atoi 仍然存在,但 Atoui 和 Atof32 不存在,因此它們可能需要手動新增的強制轉換;go fix 工具將對此發出警告。
template 包
template 和 exp/template/html 包已移至 text/template 和 html/template。更重要的是,這些包的介面已簡化。模板語言相同,但“模板集”的概念已消失,包的函式和方法也相應地發生了變化,通常是透過消除。
Template 物件不再是集合,而是可以包含多個命名模板定義,實際上為模板呼叫構建了名稱空間。模板可以呼叫與其關聯的任何其他模板,但只能呼叫與其關聯的模板。關聯模板的最簡單方法是將它們一起解析,這在新包結構下變得更容易。
更新:匯入將由 fix 工具更新。單模板使用基本上不受影響。使用多個模板協同工作的程式碼需要手動更新。text/template 文件中的示例可以提供指導。
testing 包
testing 包有一個型別 B,作為引數傳遞給基準測試函式。在 Go 1 中,B 有新方法,類似於 T 的方法,可以啟用日誌記錄和故障報告。
func BenchmarkSprintf(b *testing.B) {
// Verify correctness before running benchmark.
b.StopTimer()
got := fmt.Sprintf("%x", 23)
const expect = "17"
if expect != got {
b.Fatalf("expected %q; got %q", expect, got)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
fmt.Sprintf("%x", 23)
}
}
更新:現有程式碼不受影響,儘管使用 println 或 panic 的基準測試應該更新為使用新方法。
testing/script 包
testing/script 包已被刪除。它是一個遺留物。
更新:幾乎沒有程式碼會受到影響。
unsafe 包
在 Go 1 中,函式 unsafe.Typeof、unsafe.Reflect、unsafe.Unreflect、unsafe.New 和 unsafe.NewArray 已被刪除;它們複製了 reflect 包提供的更安全的功能。
更新:使用這些函式的程式碼必須重寫以使用 reflect 包。對 encoding/gob 和 協議緩衝區庫 的更改可能有助於作為示例。
url 包
在 Go 1 中,url.URL 型別中的幾個欄位已被刪除或替換。
String 方法現在可預測地使用 URL 的所有欄位(如有必要)重建編碼的 URL 字串。生成的字串也不再對密碼進行轉義。
Raw 欄位已被刪除。在大多數情況下,可以使用 String 方法代替。
舊的 RawUserinfo 欄位由 User 欄位替換,型別為 *net.Userinfo。可以使用新的 net.User 和 net.UserPassword 函式建立此型別的值。EscapeUserinfo 和 UnescapeUserinfo 函式也已消失。
RawAuthority 欄位已被刪除。相同的資訊可在 Host 和 User 欄位中獲取。
RawPath 欄位和 EncodedPath 方法已被刪除。帶根 URL(後跟斜槓的 scheme)中的路徑資訊現在僅在 Path 欄位中以解碼形式提供。有時,可能需要編碼資料才能獲取在解碼過程中丟失的資訊。這些情況必須透過訪問構建 URL 的資料來處理。
非帶根路徑的 URL,例如 "mailto:dev@golang.org?subject=Hi",也以不同方式處理。OpaquePath 布林欄位已被刪除,並引入了一個新的 Opaque 字串欄位來儲存此類 URL 的編碼路徑。在 Go 1 中,引用的 URL 解析為:
URL{
Scheme: "mailto",
Opaque: "dev@golang.org",
RawQuery: "subject=Hi",
}
URL 中添加了一個新的 RequestURI 方法。
ParseWithReference 函式已重新命名為 ParseWithFragment。
更新:使用舊欄位的程式碼將無法編譯,並且必須手動更新。語義更改使得 fix 工具難以自動更新。
go 命令
Go 1 引入了 go 命令,這是一個用於獲取、構建和安裝 Go 包和命令的工具。go 命令廢棄了 makefile,而是使用 Go 原始碼來查詢依賴項並確定構建條件。大多數現有 Go 程式將不再需要 makefile 來構建。
請參閱 如何編寫 Go 程式碼 獲取 go 命令的入門教程,並參閱 go 命令文件 獲取完整詳細資訊。
更新:依賴 Go 專案舊的基於 makefile 的構建基礎設施(Make.pkg、Make.cmd 等)的專案應切換到使用 go 命令來構建 Go 程式碼,並且如有必要,重寫其 makefile 以執行任何輔助構建任務。
cgo 命令
在 Go 1 中,cgo 命令使用不同的 _cgo_export.h 檔案,該檔案是為包含 //export 行的包生成的。_cgo_export.h 檔案現在以 C 前導註釋開頭,以便匯出的函式定義可以使用在那裡定義的型別。這導致前導被多次編譯,因此使用 //export 的包不能將函式定義或變數初始化放在 C 前導中。
打包釋出
與 Go 1 相關的最重要變化之一是預打包、可下載發行版的可用性。它們適用於許多架構和作業系統組合(包括 Windows),並且列表將不斷增長。安裝詳情在入門頁面上描述,而發行版本身則在下載頁面上列出。