怎么知道某個 API 是在哪個 Go 版本添加的?這個功能如何實現(xiàn)的
大家好,我是站長 polarisxu。
因為 Go 的兼容性做的很好,很多人不太關(guān)心 Go 的具體版本。然而有時候可能會涉及到版本的問題,比如你想使用 strings.Builder,Go 版本就必須 >= 1.10,但以下代碼在 Go1.10 卻編譯不通過。
- package main
- import (
- "fmt"
- "strings"
- )
- func main() {
- var b strings.Builder
- b.WriteString("polarisxu")
- fmt.Println(b.Cap())
- }
編譯會報錯:
- $ go version
- go version go1.10.8 darwin/amd64
- $ go run main.go
- # command-line-arguments
- ./main.go:11:15: b.Cap undefined (type strings.Builder has no field or method Cap)
提示 strings.Builder 類型沒有 Cap 字段或方法。
所以,你知道標準庫中哪個 API 是什么版本引入的嗎?或者更實際的是,我當前的版本是否能使用某個 API。
01 常見的兩種方式
在 Go 官網(wǎng)有最新穩(wěn)定版本的標準庫文檔。從 Go1.11 版本開始,在標準庫中,每個類型、函數(shù)或方法有加入的版本信息,如果沒有,表示 Go1.0 就有了,具體 issue 見:https://github.com/golang/go/issues/5778。但目前常量和變量沒有版本信息,具體 issue 見:https://github.com/golang/go/issues/29204。
第二種方法,不是看具體某個 API 對應的版本,而是至少知曉,你當前使用的 Go 版本有沒有某個 API,這就是 pkg.go.dev,具體通過這個網(wǎng)站 https://pkg.go.dev/std?tab=versions 選擇你對應的版本,然后查找是否有對應的 API。
當然了,你使用 GoLand 之類的編輯器,某個 API 是否有,它會自動提示。
02 標準庫顯示版本是如何實現(xiàn)的
保持好奇心很重要,這是求知的動力之一。看到官網(wǎng)標準庫顯示了版本信息,我就想看看它是怎么實現(xiàn)的。
怎么查找實現(xiàn)的代碼?
我的第一反應是看標準庫注釋里有沒有寫。
- // A Builder is used to efficiently build a string using Write methods.
- // It minimizes memory copying. The zero value is ready to use.
- // Do not copy a non-zero Builder.
- type Builder struct {
- addr *Builder // of receiver, to detect copies by value
- buf []byte
- }
沒有看到任何版本相關(guān)信息。這時你會如何查找?
我的方式是這樣的。
1)在頁面審查元素,看到 1.10 節(jié)點。
2)Go 官網(wǎng)源碼在這里:https://github.com/golang/website,在該源碼中搜索 Added in,找到了 package.html 模板文件。
3)上圖中, $since 變量代表了 Go 版本,而它是通過 since 函數(shù)得到的:`{{.PDoc.ImportPath}}`,很顯然這是一個自定義模板函數(shù),因此查找它。website 項目沒有找到,因此到 tools[1] 項目去找:因為 godoc 在這個項目中。
通過這個可以找到 sinceVersionFunc 所在文件:versions.go,然后就能找到如下的代碼:
- // InitVersionInfo parses the $GOROOT/api/go*.txt API definition files to discover
- // which API features were added in which Go releases.
- func (c *Corpus) InitVersionInfo() {
- var err error
- c.pkgAPIInfo, err = parsePackageAPIInfo()
- if err != nil {
- // TODO: consider making this fatal, after the Go 1.11 cycle.
- log.Printf("godoc: error parsing API version files: %v", err)
- }
- }
- func parsePackageAPIInfo() (apiVersions, error) {
- var apiGlob string
- if os.Getenv("GOROOT") == "" {
- apiGlob = filepath.Join(build.Default.GOROOT, "api", "go*.txt")
- } else {
- apiGlob = filepath.Join(os.Getenv("GOROOT"), "api", "go*.txt")
- }
- files, err := filepath.Glob(apiGlob)
- if err != nil {
- return nil, err
- }
- vp := new(versionParser)
- for _, f := range files {
- if err := vp.parseFile(f); err != nil {
- return nil, err
- }
- }
- return vp.res, nil
- }
通過以上代碼可以看出來版本信息是通過讀取 GOROOT 下 api/go*.txt 文件獲取的。
api 目錄下的這些文件維護了每個版本新增的內(nèi)容。
最終從這些文件中讀取的內(nèi)容會用以下的類型表示:
- // pkgAPIVersions contains information about which version of Go added
- // certain package symbols.
- //
- // Only things added after Go1 are tracked. Version strings are of the
- // form "1.1", "1.2", etc.
- type pkgAPIVersions struct {
- typeSince map[string]string // "Server" -> "1.7"
- methodSince map[string]map[string]string // "*Server" ->"Shutdown"->1.8
- funcSince map[string]string // "NewServer" -> "1.7"
- fieldSince map[string]map[string]string // "ClientTrace" -> "Got1xxResponse" -> "1.11"
- }
這里有類型、方法、函數(shù)和(類型)字段,但沒有變量和常量,這也就是說變量和常量的版本號顯示還未實現(xiàn)。
最后,在 website 項目的 main 函數(shù)中有這么一句:
- // Initialize the version info before readTemplates, which saves
- // the map value in a method value.
- corpus.InitVersionInfo()
用于初始化版本信息。
03 總結(jié)
希望你平時生活、學習和工作過程中,能多一些好奇。本文是一個引子,內(nèi)容不太重要,過程希望能夠?qū)δ阌兴鶈l(fā)。當然,如果你計劃學習學習 Go 語言官網(wǎng)的實現(xiàn),也許本文的幫助會更大。
參考資料
[1]tools: https://github.com/golang/tools
本文轉(zhuǎn)載自微信公眾號「polarisxu」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系polarisxu公眾號。