[譯] Go 語言概覽

Go 語言概覽

本文摘要:本文很是籠統地總結了 Go 語言的定義、生態系統和實現方式,也盡力給出了與不一樣的需求所對應的參考文檔,詳情參見本文末尾。html

每當咱們提及「Go 語言」的時候,可能會由於場景的不一樣聊到不少徹底不一樣的東西。所以,我嘗試着對 Go 語言和其生態系統作一個概述,並在各部份內容中都列出相關的文檔(這可能有點像是大雜燴,其中還包含了我最近實際遇到的許多問題)。讓咱們開始吧:前端

Go 編程語言

Go 語言是一種編程語言。做爲一種權威,Go 語言規範中定義了代碼的格式規範和代碼所表明的含義。不符合該規範的都不是 Go 語言。一樣地,該規範中沒有提到的內容不視爲該語言的一部分。目前由 Go 語言開發團隊維護該規範,每半年發佈一個新版本。在我寫這篇文章的時候最新的版本是 1.12android

Go 語言規範規定了:ios

  • 語法
  • 變量的類型、值,及其語義
  • 預先聲明的標識符及其含義
  • Go 程序的運行方式
  • 特殊的 unsafe 包(雖然沒有包含全部的語義)

該規範應該已經足夠讓你實現一個 Go 語言的編譯器了。實際上,已經有不少人基於此實現了許多不一樣的編譯器。git

Go 編譯器及其運行時

該語言規範只是一份文本文檔,它自己不太有用。你須要的是實現了這些語義的軟件,即編譯器(分析、檢查源代碼,並將其轉換爲可執行的形式)和運行時(提供運行代碼時所需的環境)。有不少這樣的軟件組合,他們都或多或少有些不一樣。示例以下:程序員

  • gc,Go 語言開發團隊本身開發的純 Go 語言實現的(有一小部分彙編實現)編譯器和運行時。它隨着 Go 語言一塊兒發佈。與其餘此類工具不一樣的是,gc 並不嚴格區分編譯器、組裝器和連接器 —— 它們在實現的時候共享了大量的代碼,而且會共享或傳遞一些重要職責。所以,一般沒法連接由不一樣版本的 gc 所編譯的包。
  • gccgo 和 libgo,gcc 的前端和其運行時。它是用 C 實現的,而且也由 Go 開發團隊維護。然而,因爲它是 gcc 組織的一部分,並根據 gcc 的發佈週期發佈,所以一般會稍微落後於 Go 語言規範的「最新」版本。
  • llgo,LLVM 的前端。我對其不太瞭解。
  • gopherjs,將 Go 代碼編譯爲 JavaScript,並使用一個 JavaScript VM 和一些自定義代碼做爲運行時。長遠來看,因爲 gc 得到了 WebAssembly 的原生支持,它有可能會被淘汰。
  • tinygo,針對小規模編程的不完整實現。它能夠經過自定義一個運行時運行在微控制器(裸機)或者 WebAssembly 虛擬機上。因爲它的侷限性,技術上來講它並無實現 Go 語言的全部特性 —— 主要體如今它缺乏垃圾回收器、併發和反射。

還有更多其餘的實現,但這已經足以讓你瞭解不一樣的實現方式。以上每一種方法都使用了不一樣的方式來實現 Go 語言,並具備本身不同凡響的特性。他們可能存在的不一樣之處有(爲了說明這一點,下面的某些說法可能會有點奇特):github

  • int/uint 的大小 —— 長度可能爲 32 位或 64 位。
  • 運行時中基礎功能的實現方式,如內存分配、垃圾回收和併發的實現。
  • 遍歷 map 的順序並無在 Go 語言中定義 —— gc 顯然會將這類操做隨機化,而 gopherjs 會用你使用的 JavaScript 實現遍歷。
  • append 操做分配的所需額外內存空間大小 —— 可是,在分配額外空間時不會再次分配更多的內存空間。
  • unsafe.Pointeruintptr 之間的轉換方式。特別指出,gc 對於該轉換什麼時候應該生效有本身的規則。一般狀況下,unsafe 包是虛擬的,它會在編譯器中被實現。

通常來講,根據規範中沒有提到的某些細節(尤爲是上面提到的那些細節)可使你的程序用不一樣的編譯器也能編譯,但每每程序不會像你預期的那樣正常工做。所以,你應該盡力避免此類事情發生。golang

若是你的 Go 語言是經過「正常」渠道安裝的話(在官網上下載安裝,或是經過軟件包管理器安裝),那麼你會獲得 Go 開發團隊提供的 gc 和正式的運行時。在本文中,當咱們在討論「Go 是如何作的」時,若沒有在上下文特別指明,咱們一般就是在談論 gc。由於它是最重要的一個實現。編程

標準庫

標準庫是 Go 語言中附帶的一組依賴包,它能夠被用來當即構建許多實用的應用程序。它也由 Go 開發團隊維護,而且會隨着 Go 語言和編譯器一塊兒發佈。通常來講,標準庫的某種實現只能依賴與其共同發佈的編譯器才能正常使用。由於大部分(但不是全部)運行時都是標準庫的一部分(主要包含在 runtimereflectsyscall 包中)。因爲編譯器在編譯時須要兼容當前使用的運行時,所以它們的版本要相同。標準庫的 API 是穩定的,不會以不兼容的方式改變,因此基於某個指定版本的標準庫編寫的 Go 程序在編譯器的將來版本中也能夠正常運行。c#

有些標準庫會徹底本身實現整個庫中的全部內容,而有些則只實現一部分 —— 開發者尤爲會在 runtimereflectunsafesyscall 包中實現自定義的功能。舉個例子,我相信 AppEngine 標準庫是出於安全考慮從新實現了標準庫的部分功能的。這類從新實現的部分一般會盡可能對用戶保持透明。

還存在一種標準庫之外的獨立庫,通俗地說這就是 x 或者說是「擴展庫」。這種庫包含了 Go 開發團隊同時開發和維護的部分代碼,可是不會與 Go 語言有相同的發佈週期,而且相比於 Go 語言自己,兼容性也會較差(功能性和維護性也會較差)。其中的代碼要麼是實驗性的(在將來可能會包含在標準庫中),要麼是比起標準庫中的功能還不夠泛用,或者是在某些罕見的狀況下,提供一種開發者們能夠與 Go 開發團隊同步進行代碼審查的方式。

再一次強調,若是沒有額外地指出,在提到「標準庫」時,咱們指的是官方維護和發佈的、託管在 golang.org 上的 Go 標準庫。

代碼構建工具

咱們須要代碼構建工具來使 Go 語言易於使用。構建工具的主要職責是找到須要編譯的包和全部的依賴項,並依據必要的參數調用編譯器和連接器。Go 語言有對包的支持,容許在編譯時把多個源代碼文件視爲一個單元。這也定義了導入和使用其餘包的方式。但重要的是,這並無定義導入包的路徑與源文件的映射方式,也沒有定義導入包在磁盤中的位置。所以,每種構建工具對於該問題都有不一樣的處理方式。你可使用通用構建工具(如 Make 命令),但也有許多專門爲 Go 語言而生的構建工具:

  • Go 語言工具[1]是 Go 開發團隊官方維護的構建工具。它與 Go 語言(gc 和標準庫)有相同的發佈週期。它須要一個名爲 GOROOT 的目錄(該值從環境變量中獲取,會在安裝時產生一個默認值)來存放編譯器、標準庫和其餘各類工具。它要求全部的源代碼都要存放在一個名爲 GOPATH 的目錄下(該值也從環境變量中獲取,默認爲 $HOME/go 或是一個與其相等的值)。舉例來講,包 a/b 的源代碼應該位於諸如 $GOPATH/src/a/b/c.go 的路徑下。而且 $GOPATH/src/a/b 路徑下應該包含一個包下的源文件。在分佈式的模式下,有一種機制能夠從任意服務器上遞歸地下載某個包及其依賴項,即便這種機制不支持版本控制或是下載校驗。Go 語言工具中也包含了許多其餘工具包,包括用於測試 Go 代碼的工具、閱讀文檔的工具(golang.org 是用 Go 語言工具部署的)、提交 bug 的工具和其餘各類小工具。
  • gopherjs 自帶的構建工具,它在很大程度上模仿了 Go 語言工具。
  • gomobile 是一個專門爲移動操做系統構建 Go 代碼的工具。
  • depgbglide 等等是社區開發的構建和依賴項管理工具,它們各自都有本身獨特的文件佈局方式(有些能夠與 Go 語言工具兼容,有些則不兼容)和依賴項聲明方式。
  • bazel 是谷歌內部構建工具的開源版本。雖然它的使用實際上並不限於 Go 語言,但我之因此把它列爲單獨的一項,是由於人們常說 Go 語言工具旨在爲谷歌服務,而與社區的需求相沖突。然而,Go 語言工具(和其餘許多開放的工具)是沒法被谷歌所使用的,緣由是 bazel 使用了不兼容的文件佈局方式。

代碼構建工具是大多數用戶在編寫代碼時直接使用的重要工具,所以它很大程度上決定了 Go 語言生態系統的方方面面,也決定了包的組合方式,這也將影響 Go 程序員之間的溝通和交流方式。如上所述,Go 語言工具是被隱式引用的(除非指定了其餘的運行環境),所以它的設計會讓公衆對 「Go 語言」的見解形成很大的影響。雖然有許多替代工具可供使用,這些工具也已經在如公司內部使用等場景被普遍使用,可是開源社區一般但願 Go 語言工具與 Go 語言的使用方式相契合,這意味着:

  • 能夠獲取源代碼。Go 語言工具對包的二進制分發只作了極其有限的支持,而且僅有的支持將會在未來的版本中移除。
  • 要依據 Go 官方文檔編排格式來撰寫文檔。
  • 包含測試用例,而且能經過 go test 運行測試。
  • 能夠徹底經過 go build 來編譯(與後面所述的特徵共同被稱爲「能夠經過 Go 獲得的」 —— 「go-gettable」)。特別指出,若是須要生成源代碼或是元編程,則使用 go generate 並提交生成的構件。
  • 經過命名空間導入的路徑其第一部分是一個域名,該域名能夠是一個代碼託管服務器或者是該服務器上運行的一個 Web 服務,則 Go 代碼能夠找到源代碼和其依賴,而且能夠正常工做
  • 每一個目錄都只有一個包,而且可使用代碼構建約束條件進行條件編譯。

Go 語言工具的文檔很是全面,它是一個學習 Go 如何實現各類生態系統的良好起點。

其餘工具

Go 語言的標準庫包含了一些能夠與 Go 源代碼交互的包包含了更多功能的 x/tools 擴展庫。Go 語言也所以在社區中有很是強的第三方工具開發文化(因爲官方強烈地想要保持 Go 語言自己的精簡)。這些工具一般須要知道源代碼的位置,可能還須要獲取類型信息。go/build 包遵循了 Go 語言工具的約定,所以它自己就能夠做爲其部分構建過程的文檔。缺點則是,構建在它之上的工具備時與基於其餘構建工具的代碼不兼容。所以有一個新的包正在開發中,它能夠與其餘構建工具很好地集成。

實際上 Go 語言的工具備很是多,而且每一個人都有本身的偏好。但大體以下:

總結

我想用一個簡短的參考文獻列表來結束這篇文章,列表的內容是爲那些感到迷茫的初學者準備的。請點擊下面的連接:

除此之外還有許多有價值的文檔能夠做爲補充,但這些應該已經足夠讓你有一個良好的開端了。做爲一個 Go 語言的初學者,若是你發現本文有任何遺漏之處(我可能會補充更多的細節)或者你找到了任何有價值的參考資料,請經過 Twitter 聯繫我。若是你已是一個經驗豐富的 Go 語言開發者,而且你發現我遺漏了某些重要的內容(可是我有意忽略了一些重要的參考資料,使得初學者們能夠感覺到 Go 語言學習中的新鮮感:smile:),也請給我留言。


[1] 注:Go 開發團隊目前正在對模塊作一些支持,模塊是包之上的代碼分發單元,這些支持包括版本控制和一些可使「傳統」 Go 語言工具解決問題的基礎工做。等這些支持完成之後,這一段中的全部內容基本上就都過期了。對模塊的支持目前是有的,但還不是 Go 語言的一部分。因爲本文的核心內容是對 Go 語言的不一樣組成部分進行簡要介紹,這些內容是不太容易發生變化的,目前來看我認爲理解這些歷史問題也是頗有必要的。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索