Go 1 版本資訊

Go 1 簡介

Go 1 版本(簡稱 Go 1)定義了一種語言以及一套核心函式庫,這些函式庫提供了穩定的基礎,可以用來建立可靠的產品、專案和文件。

推動 Go 1 的主要動力,是為其使用者提供穩定性。人們應能夠撰寫 Go 程式,並且預期它們將繼續編譯和執行而不會改變,時間尺度為多年,包括在類似 Google App Engine 等生產環境中。同樣地,人們應能夠撰寫有關 Go 的書籍,能夠說明這本書描述的是 Go 的哪個版本,並且這個版本號在很久以後仍然有意義。

在 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 指令會在適當的地方省略顯性格元素類型,以及其他動作。

初始化期間的 Goroutine

舊的語言定義在初始化期間執行的 go 陳述式會建立 Goroutine,但直到整個程式初始化完成後才會開始執行。這在多個地方造成笨拙的狀況,並且有效限制了 init 建構式的實用性:如果其他套件可以在初始化期間使用這個函式庫,函式庫就被迫避免使用 Goroutine。這個設計是出於簡潔性和安全性的考量,但隨著我們對這門語言信心增長,看來這沒有必要。在初始化期間執行 Goroutine 並不比在正常執行期間執行更加複雜或不安全。

在 Go 1 中,可以在 init 常式和全域初始化表達式呼叫使用 Goroutine 的程式碼,而且不會造成僵局。

var PackageGlobal int

func init() {
    c := make(chan int)
    go initializationFunction(c)
    PackageGlobal = <-c
}

更新:這是新的功能,所以既有程式碼不需要變更,儘管有可能依賴在 main 之前不會啟動的 Goroutine 的程式碼會損毀。標準儲存庫中沒有這樣的程式碼。

rune 類型

語言規格允許 int 類型為 32 或 64 位元寬,但目前的實作將 int 設定為 32 位元,即使在 64 位元平台上也是如此。在 64 位元平台上,最好讓 int 為 64 位元。(索引大型切片有重要的後果。)但是,這個變更會在使用舊語言處理 Unicode 字元時浪費空間,因為 int 類型也用於儲存 Unicode 碼點:如果 int 從 32 位元增加到 64 位元,每個碼點會浪費額外的 32 位元儲存空間。

為了讓變更為 64 位元 int 可行,Go 1 推出了新的基本類型 rune,用於代表單一的 Unicode 碼點。它是 int32 的別名,類似於 byteuint8 的別名。

'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")
    }

更新:大部分原始碼不受此影響,因為 := 初始值設定項目的類型推論會在無聲無息中引入新類型,並從那裡傳播出去。有些程式碼可能會出現類型錯誤,而這些錯誤可以用細微的轉換來解決。

錯誤類型

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 指的是預先定義的布林常數。這個修正工具會標記語法中的其他使用,以便程式設計師檢查。

在字典中反覆運算

舊語言規格沒有定義字典的反覆運算順序,並且在實務中,它在不同的硬體平台上有所不同。這使得對字典進行反覆運算的測試很脆弱且不可移植,令人不快的是,一個測試在一個機器上可能總是通過,但在另一個機器上卻會中斷。

在 Go 1 中,使用 for 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)
    }

更新中:這是一個工具無法協助的變更。大部分現有程式碼將不受影響,但有些程式可能會中斷或出現異常行為;我們建議手動檢查所有範圍陳述式(over maps)以驗證他們不會依賴於反覆運算順序。標準存放庫中有幾個這樣的範例;它們已經修復了。請注意,依賴於反覆運算順序本來就不正確,因為它沒有指定。這個變更規定了不可預測性。

多重指定

語言規格已保證在指定時,右側表達式在任何左側表達式被指定之前都經過評估。為了保證可預測的行為,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 編譯器將不允許沒有參數的回傳陳述式。(這不是規格的一部分,因為這是我們仍在探索的領域之一;此情況類似於編譯器會拒絕不以明確回傳陳述式結束的函式。)

此函式隱含地回傳一個陰影的回傳值,將會被編譯器拒絕

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.
}

更新中:以這種方式陰影回傳值的程式碼將會被編譯器拒絕,需要手動修正。標準存放庫中出現的幾個案例大多是錯誤。

複製具有未匯出的欄位的結構

舊語言不允許套件複製包含屬於不同套件的未匯出欄位的結構值。不過,函式接收者有一個必要的例外;另外,copyappend 的實作從未遵守此限制。

Go 1 將允許套件複製包含來自其他套件的未匯出欄位的結構值。除了解決不一致性之外,這個變更允許一種新的 API:套件可以在不訴諸指標或介面的情況下回傳一個不透明值。time.Timereflect.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 有重新排列的套件階層,將相關項目分組到子目錄中。例如,utf8utf16 現在位於 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
mail 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

請注意,舊的 cmathexp/template/html 套件的套件名稱已變更為 cmplxtemplate

更新:執行 go fix 將更新所有匯入和重新命名的套件,這些套件仍保留在標準儲存庫內。匯入標準儲存庫中不再存在的套件的程式碼需要手動編輯。

exp 套件樹

由於未標準化,exp 目錄下的套件將不會出現在標準 Go 1 發行版本中,儘管它們將以原始碼的形式出現在 儲存庫 中供開發人員使用。

在 Go 1 的發行版中,有幾個套件已移至 exp 之下

(EscapeStringUnescapeString 類型仍保留在套件 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 目錄中沒有這些套件,但它們仍以原始碼形式提供給想要使用這些套件的開發人員。

這些套件在新位置中

更新:需要手動更新使用目前位於 old 中的套件的程式碼,或者從提供 old 的安裝中編譯。gofix 工具會對此類使用提出警告。

已刪除的套件

Go 1 直接刪除下列幾個套件

以及 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 套件時產生,當時看來與系統有關。從那時起,就很明顯地看出,錯誤比作業系統更基本。例如,將 Error 用在 os 所依賴的套件中會很好,例如 syscall。另外,將 Error 放在 os 中會引入許多對 os 的依賴關係,否則並不存在這些關係。

Go 1 透過引入內建 error 介面類型和一個單獨的 errors 套件(類似於 bytesstrings)來解決這些問題,其中包含工具函式。它使用 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 上,實作是由滿足 errorsyscall.Errno 類型所完成,並取代舊的 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 之類的方法,可從兩個 Times 中減掉一個 Duration

最重要的語意變更在於 Unix 紀元(1970 年 1 月 1 日)現在僅與那些提到 Unix 的函式及方法相關:time.Unix 以及 Time 類型的 UnixUnixNano 方法。特別是,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 等值。此外,由於一些產生的值中類型已變更,因此一些由修正工具改寫的表達式可能需要進一步人工編輯;在這種情況下,改寫將包含適用於舊功能的正確函式或方法,但可能類型錯誤或需要進一步分析。

程式庫微小的變更

本節描述較小的變更,例如對不常使用的套件所做的變更,或除了需要執行 go fix 之外,僅影響少數程式碼的變更。此類別包含 Go 1 中新增的套件。它們會集體改善可攜性、規範化行為,並使介面更現代且更類似 Go。

archive/zip 套件

在 Go 1 中,*zip.Writer 不再擁有 Write 方法。它的存在是一個錯誤。

更新:少部分受影響的程式碼將會被編譯器偵測到,而且必須手動更新。

bufio 套件

在 Go 1 中,bufio.NewReaderSizebufio.NewWriterSize 函式不再會為無效的大小傳回錯誤。如果設定的大小過小或無效,它會進行調整。

更新:使用 go fix 會更新將錯誤指定給 _ 的呼叫。編譯器會攔截未修正的呼叫,且它們必須手動更新。

compress/flate、compress/gzip 與 compress/zlib 套件

在 Go 1 中,compress/flatecompress/gzipcompress/zlib 中的 NewWriterXxx 函式如果採用壓縮級別,則全部都會傳回 (*Writer, error);否則會傳回 *Writergzip 套件的 CompressorDecompressor 類型已更名為 WriterReaderflate 套件的 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.CurveMarshalUnmarshalGenerateKey 的呼叫現在為 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 方法已更動,它們在以前採取 *rsa.PublicKey*rsa.PrivateKey 的地方,改採 interface{}。這將允許在未來實作出其他公開金鑰演算法。

更新:無需變更。

encoding/binary 套件

在 Go 1 中,binary.TotalSize 函式已由 Size 取代,後者採用 interface{} 參數,而非 reflect.Value

更新:少部分受影響的程式碼將會被編譯器偵測到,而且必須手動更新。

encoding/xml 套件

在 Go 1 中,xml 套件的設計已接近其他編組解編器套件,例如 encoding/gob

舊有的 Parser 類型已重新命名為 Decoder 並具有新的 Decode 方法。此外,還新增了 Encoder 類型。

函式 MarshalUnmarshal 現在採用 []byte 值。如要與串流搭配使用,請使用新的 EncoderDecoder 類型。

在編組解編器數值時,欄位標籤中受支持旗標的格式已變更,以接近 json 套件 (`xml:"name,flag"`)。現在,欄位標籤、欄位名稱以及 XML 屬性和元素名稱之間的比對會區分大小寫。如果存在 XMLName 欄位標籤,則其也必須與即將編組解編的 XML 元素名稱相符。

更新: 執行 go fix 會更新套件的大多數使用方式,唯獨 Unmarshal 的部分呼叫除外。欄位標籤必須特別注意,因為修正工具不會更新它們;若未手動修正,它們在某些情況下會靜默異常。例如,舊有的 "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 格式化方式:10s1h30m 等。

var timeout = flag.Duration("timeout", 30*time.Second, "how long to wait for completion")

更新: 實作自家旗標的程式會需要進行輕微的手動修正,以更新其 Set 方法。Duration 旗標是新功能,不會影響現有程式碼。

go/* 套件

go 下的幾個套件已稍微修正了 API。

針對套件 go/scannergo/parsergo/printergo/doc 中的組態模式旗標,已引入具體的 Mode 類型。

已從 go/scanner 套件中移除 AllowIllegalCharsInsertSemis 模式。這些模式主要用於掃描 Go 原始檔以外的文字。建議改用 text/scanner 套件處理此類目的。

現在提供給掃描器 Init 方法的 ErrorHandler 只是一個函式,不再是介面。已經移除 ErrorVector 類型,轉而採用 (現有的) ErrorList 類型,並已移植 ErrorVector 方法。現在,不用在掃描器的用戶端內嵌 ErrorVector,而應該維護一個 ErrorList

go/parser 套件提供的剖析函數集合已減少為主要剖析函數 ParseFile,以及幾個便利函數 ParseDirParseExpr

go/printer 套件支援額外的組態模式 SourcePos;如果設為這個模式,印表機將發出 //line 註解,使產生的輸出包含原始原始碼位置資訊。新類型 CommentedNode 可用來提供與任意 ast.Node 相關的註解 (到目前為止,只有 ast.File 會傳送註解資訊)。

go/doc 套件的類型名稱已透過移除 Doc 字尾變得更精簡:PackageDoc 現在變成了 PackageValueDoc 變成了 Value 等。此外,所有類型現在都持續有一個 Name 欄位 (或是 Names,例如 Value 類型),而 Type.Factories 則已變成 Type.Funcs。現在可透過 doc.NewPackageDoc(pkg, importpath) 來建立套件的文件,

    doc.New(pkg, importpath, mode)

其中新的 mode 參數會指定操作模式:若設定為 AllDecls,會考量所有宣告 (而不只是外傳宣告)。已移除 NewFileDoc 函數,而 CommentText 函數已變成 ast.CommentGroupText 方法。

在套件 go/token 中,token.FileSet 方法 Files (最初會傳回一個 *token.Files 通道) 已改為迭代器 Iterate,並接受函式引數。

go/build 套件中,API 已幾近完全替換。該套件仍會計算 Go 套件資訊,但不會執行編譯:CmdScript 型別已經不見了。(改用新的 go 命令進行編譯。)DirInfo 型別現在的名稱為 PackageFindTreeScanDir 已被 ImportImportDir 取代。

更新:在 go 中使用套件的程式碼必須手動更新;編譯器會拒絕使用不正確的方式。與 go/doc 任何型別搭配使用的範本可能需要手動修正;重新命名的欄位會導致執行階段錯誤。

雜湊套件

在 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 客戶端鮮少需要這些部分。受到影響的項目包括

Request.RawURL 欄位已移除;它是一種歷史人為因素。

HandleHandleFunc 函式,以及 ServeMux 的同名方法,現在在嘗試註冊同一個模式兩次時,會發生恐慌。

更新:執行 go fix 會更新少數受到影響的程式,RawURL 用法除外,必須手動修正。

image 套件

image 套件進行了不少微小的變更、重新配置和重新命名。

大多數色彩處理程式碼都已移至其專屬套件 image/color 中。對於已移走的元素,對稱性會隨之出現;例如,image.RGBA 中的每個像素都是 color.RGBA

舊的 image/ycbcr 套件已透過一些更名動作,合併至 imageimage/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 函式 (NewRGBANewRGBA64 等) 會將 image.Rectangle 視為引數,而非四個整數。

最後,有新的預先定義的 color.Color 變數 color.Blackcolor.Whitecolor.Opaquecolor.Transparent

更新:執行 go fix 將更新幾乎所有受變更影響的程式碼。

log/syslog 套件

在 Go 1 中,syslog.NewLogger 函式會傳回錯誤以及 log.Logger

更新:少部分受影響的程式碼將會被編譯器偵測到,而且必須手動更新。

mime 套件

在 Go 1 中,mime 套件的 FormatMediaType 函式已簡化,以與 ParseMediaType 保持一致。它現在採用 "text/html",而非 "text""html"

更新:少部分受影響的程式碼將會被編譯器偵測到,而且必須手動更新。

net 套件

在 Go 1 中,各種 SetTimeoutSetReadTimeoutSetWriteTimeout 方法已分別改為 SetDeadlineSetReadDeadlineSetWriteDeadline。新方法並非接收以奈秒為單位的逾時值並套用至連線上的任何活動,而是設定絕對逾時 (為 time.Time 值),在逾時後,讀取和寫入將逾時並不再封鎖。

還新增了 net.DialTimeout 函式,以簡化計時逾時撥打網路地址,以及 net.ListenMulticastUDP 函式,以允許透過多個偵聽器並行偵聽組播 UDP。net.ListenMulticastUDP 函式取代了舊的 JoinGroupLeaveGroup 方法。

更新:使用舊方法的程式碼將無法編譯且必須手動更新。語意變更使修復工具難以自動更新。

os 套件

Time 函式已移除;呼叫者應使用 time 套件中的 Time 類型。

Exec 函式已移除;呼叫者應使用 syscall 套件中的 Exec (如果可用)。

ShellExpand 函式已重新命名為 ExpandEnv

NewFile 函式現在接收 uintptr fd,而非 int。檔案上的 Fd 方法現在也傳回 uintptr

由於值集會隨著底層作業系統而異,因此 os 套件中不再有 EINVAL 等錯誤常數。有新的可攜式函式,例如 IsPermission,用於測試常見的錯誤屬性,另有少數新錯誤值具有更類 Go 的名稱,例如 ErrPermissionErrNotExist

Getenverror 函式已移除。若要區分不存在的環境變數和空字串,請使用 os.Environsyscall.Getenv

Process.Wait 方法已移除其選項引數,且相關常數也已從套件中移除。此外,Wait 函式已移除;只有 Process 類型的 Wait 方法才存在。

Process.Wait 返回的 Waitmsg 型態已替換為功能更強大的 ProcessState 型態,且內含存取方法以取得程序資訊。由於對 Wait 的變動,ProcessState 值永遠會描述一個已結束的程序。由於為達成可移植性,依其他方式簡化了介面,但 ProcessState.SysProcessState.SysUsage 方法所回傳的值,可以型別斷言為底層特定於系統的資料結構,例如 Unix 中的 syscall.WaitStatussyscall.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 的子型態,此為純整數型態,內含 IsDirPerm、及 String 方法。

檔案模式和屬性 (在 Unix 上為 i-node) 的特定於系統的詳細資料已從 FileInfo 中移除。取而代之的是,每個作業系統的 os 套件提供 FileInfo 介面的實作,此介面內含方法 Sys,可以回傳檔案中繼資料的特定於系統的表示。例如,若要找出 Unix 系統上檔案的 i-node,請依下列方式解開 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-node 表達式可以簡寫成

    fi.Sys().(*syscall.Stat_t).Ino

絕大多數使用 FileInfo 的狀況只需要標準介面的方法。

os 套件不再包含 POSIX 錯誤的包裝函數,例如 ENOENT。對於需要驗證特定錯誤條件的少數程式,現在有布林函數 IsExistIsNotExistIsPermission

    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.FileInfoos.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.Semacquireruntime.Semrelease 的程式碼應使用管道或套件 sync 中的抽象。runtime.Allocruntime.Freeruntime.Lookup 函數為針對偵錯記憶體配置器而建立的非安全 API,沒有任何替換。

以前,runtime.MemStats 是儲存關於記憶體配置資訊的全球變數,呼叫 runtime.UpdateMemStats 來確保其為最新狀態。在 Go 1 中,runtime.MemStats 為結構型別,程式碼應使用 runtime.ReadMemStats 來取得目前的狀態。

此套件新增一個函數, runtime.NumCPU,此函數會傳回作業系統核心報告的可用於平行執行的 CPU 數目。其值可以通知 GOMAXPROCS 的設定。runtime.Cgocallsruntime.Goroutines 函數已重新命名為 runtime.NumCgoCallruntime.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 會持續存在,但 AtouiAtof32 不會,因此這些可能需要手動加入的強制轉換;go fix 工具會警告此事。

範本套件

templateexp/template/html 套件已移至 text/templatehtml/template。更重要的是,這些套件的介面已簡化。「範本集」的概念已經消失,且這些套件的函式和方法也因此有所變更,通常是透過移除來變更。

Template 物件不會再使用集,而是可能會包含多個具名稱的範本定義,實際上是在建立名稱空間以供範本呼叫。範本可以呼叫與其關聯的任何其他範本,但只能呼叫與其關聯的那些範本。關聯範本最簡單的方法就是將它們一同剖析,而套件的新架構也讓這個步驟變得更簡單。

更新中:這些匯入會由修正工具更新。單一範本用量將大致不受影響。組合使用多項範本的程式碼則需要手動更新。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)
    }
}

更新中:現有的程式碼不受影響,儘管使用 printlnpanic 的基準測試應更新為使用新方法。

testing/script 套件

testing/script 套件已被刪除。它是一個糟粕。

更新中:沒有程式碼會受到影響。

unsafe 套件

在 Go 1 中,函數 unsafe.Typeofunsafe.Reflectunsafe.Unreflectunsafe.Newunsafe.NewArray 已被移除;它們重複了套件 reflect 所提供的更安全的執行功能。

更新中:使用這些函數的程式碼必須改寫為使用套件 reflect。對 encoding/gob通訊協定緩衝程式庫 的變更可以作為範例提供說明。

url 套件

在 Go 1 中,url.URL 型別中的幾個欄位已被移除或取代。

String 現在可預測地重新建構一個編碼的 URL 字串,其中使用所有 URL 的欄位。產生的字串中也不再對密碼進行跳脫。

Raw 欄位已被移除。在多數情況下,String 方法可以用在其位置上。

舊的 RawUserinfo 欄位已被 User 欄位取代,其型別為 *net.Userinfo。此類型的值可以使用新的 net.Usernet.UserPassword 函數建立。EscapeUserinfoUnescapeUserinfo 函數也已移除。

RawAuthority 欄位已被移除。相同的資訊可在 HostUser 欄位中取得。

RawPath 欄位和 EncodedPath 方法已移除。根 URL 中的路徑資訊(在 schema 之後跟著斜線)現在只能在 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",
    }

已新增一個新的 RequestURI 方法至 URL

已將 ParseWithReference 函式改名為 ParseWithFragment

更新:使用舊欄位程式碼將無法編譯,且必須手動更新。此類語意變更讓修正工具難以自動更新。

go 命令

Go 1 新增了 go 命令,這個工具可供查詢、編譯和安裝 Go 套件和命令。go 命令會捨棄 makefile,改用 Go 原始碼來尋找相依模組,並判斷編譯條件。大多數現有的 Go 程式不再需要 makefile 即可編譯。

參閱 如何撰寫 Go 程式碼,以了解 go 命令的入門介紹,並參閱 go 命令文件 以進一步了解所有詳細資訊。

更新:仰賴舊版 Go 專案 makefile 建置架構 (例如 Make.pkgMake.cmd 等) 的專案,應轉而使用 go 命令來建置 Go 程式碼,並視需要改寫 makefile 來執行任何輔助建置工作。

cgo 命令

在 Go 1 中,cgo 命令 會使用不同的 _cgo_export.h 檔案,這會為含有 //export 行的套件產生。_cgo_export.h 檔案現在會從 C 前置註解開始,因此輸出的函式定義可以使用所定義的類型。這會有將前置註解編譯多次的效果,所以使用 //export 的套件不應將函式定義或變數初始化放在 C 前置註解中。

封裝的版本

與 Go 1 搭配的最重要變更之一,便是預先封裝且可下載的套件的普及。這些套件對多種架構和作業系統 (包括 Windows) 都可以使用,而且數量還會持續增加。安裝詳細資訊說明在 入門指南 網頁上,同時可以在 下載頁面 上找到套件清單。