Loading AI tools
編程語言 来自维基百科,自由的百科全书
Go(又稱Golang[4])是Google開發的一種靜態強型別、編譯型、並發型,並具有垃圾回收功能的程式語言。
![]() | 此條目可參照英語維基百科相應條目來擴充。 |
羅伯特·格瑞史莫、羅勃·派克及肯·湯普遜於2007年9月開始設計Go,[3]稍後伊恩·蘭斯·泰勒(Ian Lance Taylor)、拉斯·考克斯(Russ Cox)加入專案。Go是基於Inferno作業系統所開發的。[5]Go於2009年11月正式宣佈推出,成為開放原始碼專案,支援Linux、macOS、Windows等作業系統。[6]
在2009年與2016年,Go被軟件評價公司TIOBE選為「TIOBE 2016年最佳語言」。[7][8]
目前,Go每半年發佈一個二級版本(即從a.x升級到a.y)。
Go的語法接近C語言,但對於變數的聲明有所不同。Go支援垃圾回收功能。Go的平行計算模型是以東尼·霍爾的交談循序程式(CSP)為基礎,採取類似模型的其他語言套件括Occam和Limbo[3],Go也具有這個模型的特徵,比如通道傳輸。通過goroutine和通道等並列構造可以建造線程池和管道等[9]。在Go 1.8版本中開放外掛程式(Plugin)的支援,這意味着現在能從Go中動態載入部分函數。
與C++相比,Go並不包括如列舉、例外處理、繼承、泛型(此功能在Go 1.18版本中加入)、斷言、虛擬函式等功能,但增加了切片(Slice)類型、並行、管道、垃圾回收、介面等特性的語言級支援[3]。對於斷言的存在,則持負面態度,同時也為自己不提供型別繼承來辯護。
儘管 Go 的官方與支持者對於語言中不使用泛型與異常處理有着大量辯解說詞,但批評聲也從未停過。在發表 Go 語言 2.0 的草案時,官方稱沒有泛型、異常處理與模組對於 Go 發展造成很大的阻礙[10],等同承認 Go 沒有這些特色是設計錯誤。
Go 的垃圾回收機制一直被人詬病,直到 1.8 版本垃圾回收的功能才較為穩定。然而儘管如此,Go 的垃圾回收還是遠遠落後 JVM 的 G1 和 ZGC。Discord 的研發團隊在2020年初甚至發表一篇網誌,宣佈把其中一個服務由從 Go 轉移至 Rust,理由是 Go 的垃圾回收會導致每2分鐘出現一次卡頓,並且 Discord 研發團隊測試了 Go 語言的1.8、1.9、1.10版本這個問題都沒有改善[11]。
2007年,Google設計Go,目的在於提高在多核、網絡機器(networked machines)、大型代碼庫(codebases)的情況下的開發效率。[12]當時在Google,設計師們想要解決其他語言使用中的缺點,但是仍保留他們的優點。[13]
設計師們主要受他們之間流傳的「不要像C++」啟發。[15][16][17]
Go於2009年11月正式宣佈推出,[18]版本1.0在2012年3月發佈。[19][20]之後,Go廣泛應用於Google的產品[21]以及許多其他組織和開源專案。
在2016年11月,Go(一種無襯線體)和Go Mono 字型(一種等寬字型)分別由設計師查爾斯·比格洛和克莉絲·荷姆斯發佈。兩種字型均採用了WGL4,並且依照着 DIN 1450 標準,可清晰地使用了 large x-height 和 letterforms 。[22][23]
在2018年8月,原生的圖示更換了。待描述完整 然而,Gopher mascot 仍舊命相同的名字。[24]
在2018年8月,Go的主要貢獻者發佈了兩個關於語言新功能的「草稿設計——泛型和例外處理,同時尋求Go用戶的反饋。[25][26]Go 由於在1.x時,缺少對泛型編程的支援和冗長的例外處理而備受批評。
此條目或章節需要時常更新。有關事物或許會隨着時間而有所變化。 |
Go 1 保證語言規範[27]和標準庫的主要部分相容。直到目前的 Go 1.20 發佈[28],所有版本都履行了這個承諾。 每個主要的 Go 發佈版本會受到支援,直到出現兩個新的主要版本為止。[29]
Release | Status | Release date | Maintenance end |
---|---|---|---|
go1 | End-of-Life | 2012-03-28 | 2013-12-01 |
go1.1 | End-of-Life | 2013-05-13 | 2014-06-18 |
go1.2 | End-of-Life | 2013-12-01 | 2014-12-10 |
go1.3 | End-of-Life | 2014-06-18 | 2015-08-19 |
go1.4 | End-of-Life | 2014-12-10 | 2016-02-17 |
go1.5 | End-of-Life | 2015-08-19 | 2016-08-15 |
go1.6 | End-of-Life | 2016-02-17 | 2017-02-16 |
go1.7 | End-of-Life | 2016-08-15 | 2017-08-24 |
go1.8 | End-of-Life | 2017-02-16 | 2018-02-16 |
go1.9 | End-of-Life | 2017-08-24 | 2018-08-24 |
go1.10 | End-of-Life | 2018-02-16 | 2019-02-25 |
go1.11 | End-of-Life | 2018-08-24 | 2019-09-03 |
go1.12 | End-of-Life | 2019-02-25 | 2020-02-25 |
go1.13 | End-of-Life | 2019-09-03 | 2020-08-11 |
go1.14 | End-of-Life | 2020-02-25 | 2021-02-16 |
go1.15 | End-of-Life | 2020-08-11 | 2021-08-16 |
go1.16 | End-of-Life | 2021-02-16 | 2022-03-15 |
go1.17 | End-of-Life | 2021-08-16 | 2022-08-02 |
go1.18 | End-of-Life | 2022-03-15 | 2023-02-01 |
go1.19 | End-of-Life | 2022-08-02 | 2023-08-08 |
go1.20 | End-of-Life | 2023-02-01 | Q1 2024 |
go1.21 | Maintenance | 2023-08-08 | Q3 2024 |
go1.22 | Current | 2024-02-06 | Q1 2025 |
格式: 舊版本 舊版本,仍被支援 目前版本 未來版本 |
下面是用Go寫成的Hello World程式:
package main
import "fmt"
func main() {
fmt.Println("Hello, World")
}
透過Go僅需幾行程式碼就完成HTTP網頁伺服器的實現:
package main
import (
"io"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world!")
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":8000", nil)
}
下面的例子說明了怎樣用Go去實現一個像Unix中的Echo命令程式[30]:
package main
import (
"os"
"flag"
)
var omitNewline = flag.Bool("n", false, "don't print final newline")
const (
Space = " "
Newline = "\n"
)
func main() {
flag.Parse() // Scans the arg list and sets up flags
var s string = ""
for i := 0; i < flag.NArg(); i++ {
if i > 0 {
s += Space
}
s += flag.Arg(i)
}
if !*omitNewline {
s += Newline
}
os.Stdout.WriteString(s)
}
Go有定義如下的撰寫風格:
;
。{
不能夠換行放置。if
判斷式和for
迴圈不需要以小括號包覆起來。除了第二點外(換行會產生編譯錯誤),在不符合上述規定時,仍舊可以編譯,但使用了內建gofmt工具後,會自動整理程式碼,使之符合規定的撰寫風格。
Go 採用 module 的概念(於 go1.11才開始啟用[31],舊版本請參閱工作區),每個專案都是一個 module ,而每個 module 底下會有個 go.mod
的檔案,來管理該 module 所參照的外部庫、開發版本……等等。
一個 module 的資料夾目錄可能如下
go.mod hello/ hello.go outyet/ main.go main_test.go # 測試用的程式 stringutil/ reverse.go reverse_test.go # 測試用的程式 bmp/ reader.go writer.go
然後 go.mod 的內容可能為
module example.org/go-mod-sample go 1.11 require ( github.com/golang/example v0.0.0-20220412213650-2e68773dfca0 golang.org/x/image v0.1.0 )
Go的工作區位於GOPATH
,其目錄結構如下[32]:
src pkg bin
三個目錄的用途分別為
舉例來說,整個專案目錄可能會如下:
bin/ hello # 生成的執行檔 outyet # 生成的執行檔 pkg/ linux_amd64/ github.com/golang/example/ stringutil.a # 編譯時,生成的對象檔案 src/ github.com/golang/example/ .git/ # 外部 Git 庫的詮釋資料 hello/ hello.go # Git 庫的程式碼 outyet/ main.go # Git 庫的程式碼 main_test.go # Git 庫的程式碼(測試用的程式) stringutil/ reverse.go # Git 庫的程式碼 reverse_test.go # Git 庫的程式碼(測試用的程式) golang.org/x/image/ .git/ # 外部 Git 庫的詮釋資料 bmp/ reader.go # Git 庫的程式碼 writer.go # Git 庫的程式碼
Go的主要特色在於易於使用的併行設計,叫做Goroutine,透過Goroutine能夠讓程式以非同步的方式執行,而不需要擔心一個函數導致程式中斷,因此Go也非常地適合網絡服務。假設有個程式,裏面有兩個函數:
func main() {
// 假設 loop 是一個會重複執行十次的迴圈函式。
// 迴圈執行完畢才會往下執行。
loop()
// 執行另一個迴圈。
loop()
}
這個時候透過Go讓其中一個函數同步執行,如此就不需要等待該函數執行完後才能執行下一個函數。
func main() {
// 透過 `go`,我們可以把這個函式同步執行,
// 如此一來這個函式就不會阻塞主程式的執行。
go loop()
// 執行另一個迴圈。
loop()
}
Goroutine是類似線程的概念,屬於纖程(區別於協程和線程)。線程屬於系統層面,通常來說建立一個新的線程會消耗較多的資源且管理不易;而協程的主要作用是提供在一個線程內的併發性,卻不能利用多個處理器線程。而 Goroutine就像輕量級的線程,一個Go程式可以執行超過數萬個 Goroutine[33],並且這些效能都是原生級的,隨時都能夠關閉、結束,且運行在多個處理器線程上。一個核心裏面可以有多個Goroutine,透過GOMAXPROCS參數你能夠限制Gorotuine可以佔用幾個系統線程來避免失控。
在內建的官方套件中也不時能夠看見Goroutine的應用,像是net/http中用來監聽網絡服務的函數實際上是建立一個不斷執行迴圈的Goroutine;同時搭配了epoll 等IO多路復用機制維護Goroutine的事件迴圈。
當前有兩個Go編譯器分支,分別為官方編譯器gc和gccgo。官方編譯器在初期使用C寫成,後用Go重寫從而實現自舉[34]。Gccgo是一個使用標準GCC作為後端的Go編譯器[35]。
官方編譯器支援跨平台編譯(但不支援CGO),允許將原始碼編譯為可在目標系統、架構上執行的二進制檔案。
由於go的原生跨平台,以及大量的官方庫,被用於大量開源程式,例如:docker、Syncthing、ipfs、Hugo、caddy、以太坊、V2Ray、Gitea、TiDB。