Go

Go（又稱 Golang）是 Google 開發的一種靜態強型別、編譯型、併發型，並具有垃圾回收功能的程式語言。

、羅勃・派克及肯・湯普遜於 2007 年 9 月開始設計 Go，稍後伊恩・蘭斯・泰勒（Ian Lance Taylor）、拉斯・考克斯（Russ Cox）加入專案。Go 是基於 Inferno 作業系統所開發的。Go 於 2009 年 11 月正式宣佈推出，成為開放原始碼專案，支援 Linux、macOS、Windows 等作業系統。

在 2009 年與 2016 年，Go 被軟體評價公司選為「TIOBE 2016 年最佳語言」。

目前，Go 每半年釋出一個二級版本（即從 a.x 升級到 a.y）。

描述

Go 的語法接近 C 語言，但對於變數的宣告有所不同。Go 支援垃圾回收功能。Go 的平行計算模型是以東尼・霍爾的通訊順序程序（CSP）為基礎，採取類似模型的其他語言包括 Occam 和 Limbo，Go 也具有這個模型的特徵，比如通道傳輸。透過 goroutine 和通道等並行構造可以建造執行緒池和管道等。在 Go 1.8 版本中開放外掛（Plugin）的支援，這意味著現在能從 Go 中動態載入部分函式。

與 C++ 相比，Go 並不包括如列舉、異常處理、繼承、泛型（此功能在 Go 1.18 版本中加入）、斷言、虛擬函式等功能，但增加了切片（Slice）型別、併發、管道、垃圾回收、介面等特性的語言級支援。對於斷言的存在，則持負面態度，同時也為自己不提供型別繼承來辯護。

不同於 Java，Go 原生提供了關聯陣列（也稱為雜湊表（Hashes）或字典（Dictionaries））。

批評

儘管 Go 的官方與支持者對於語言中不使用泛型與異常處理有著大量辯解說詞，但批評聲也從未停過。在發表 Go 語言 2.0 的草案時，官方稱沒有泛型、異常處理與模組對於 Go 發展造成很大的阻礙，等同承認 Go 沒有這些特色是設計錯誤。

Go 的垃圾回收機制一直被人詬病，直到 1.8 版本垃圾回收的功能才較為穩定。然而儘管如此，Go 的垃圾回收還是遠遠落後 JVM 的 G1 和 ZGC。Discord 的研發團隊在 2020 年初甚至發表一篇部落格，宣佈把其中一個服務從 Go 轉移至 Rust，理由是 Go 的垃圾回收會導致每 2 分鐘出現一次卡頓，並且 Discord 研發團隊測試了 Go 語言的 1.8、1.9、1.10 版本這個問題都沒有改善。

歷史

2007 年，Google 設計 Go，目的在於提高在多核、網路機器（networked machines）、大型程式碼庫（codebases）的情況下的開發效率。當時在 Google，設計師們想要解決其他語言使用中的缺點，但是仍保留他們的優點。

靜態型別和執行時效率。（如：C++）
可讀性和易用性。（如：Python 和 JavaScript）
高效能的網路和多程序。

設計師們主要受他們之間流傳的 “不要像 C++” 啟發。

Go 於 2009 年 11 月正式宣佈推出，版本 1.0 在 2012 年 3 月釋出。之後，Go 廣泛應用於 Google 的產品以及許多其他組織和開源專案。

在 2016 年 11 月，Go（一種無襯線體）和 Go Mono 字型（一種等寬字型）分別由設計師和釋出。兩種字型均採用了，並且依照著 DIN 1450 標準，可清晰地使用了 large x-height 和 letterforms 。

在 2018 年 8 月，原生的圖示更換了。待描述完整 然而，Gopher mascot 仍舊命相同的名字。

在 2018 年 8 月，Go 的主要貢獻者釋出了兩個關於語言新功能的 “草稿設計 —— 泛型和異常處理，同時尋求 Go 使用者的反饋。Go 由於在 1.x 時，缺少對泛型程式設計的支援和冗長的異常處理而備受批評。

3.1 版本歷史

Go 1 保證語言規範和標準庫的主要部分相容。直到目前的 Go 1.20 發布，所有版本都履行了這個承諾。

每個主要的 Go 發布版本會受到支援，直到出現兩個新的主要版本為止。

程式碼示例

4.1 Hello World

下面是用 Go 寫成的 Hello World 程式：

package main

import "fmt"

func main {fmt.Println ("Hello, World")

}

4.2 HTTP 網頁伺服器

透過 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)

}

4.3 Echo 命令程式

下面的例子說明了怎樣用 Go 去實現一個像 Unix 中的 Echo 命令程式：

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 0 {s += Space}

s += flag.Arg (i)

}

if !*omitNewline {s += Newline}

os.Stdout.WriteString (s)

}

語言特徵

5.1 撰寫風格

Go 有定義如下的撰寫風格：

每行程式結束後不需要撰寫分號；。
大括號 {不能夠換行放置。
if 判斷式和 for 迴圈不需要以小括號包覆起來。
使用 tab 做排版

除了第二點外（換行會產生編譯錯誤），在不符合上述規定時，仍舊可以編譯，但使用了內建 gofmt 工具後，會自動整理程式碼，使之符合規定的撰寫風格。

5.2 專案架構

5.2.1 module

Go 採用 module 的概念（於 go1.11 才開始啟用，舊版本請參閱工作區），每個專案都是一個 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

)

5.2.2 工作區

Go 的位於 GOPATH，其目錄結構如下：

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 庫的程式碼

5.3 輕型協程

Go 的主要特色在於易於使用的併行設計，叫做 Goroutine，透過 Goroutine 能夠讓程式以非同步的方式執行，而不需要擔心一個函式導致程式中斷，因此 Go 也非常地適合網路服務。假設有個程式，裡面有兩個函式：

func main {

// 假設 loop 是一個會重複執行十次的迴圈函式。

// 迴圈執行完畢才會往下執行。

loop

// 執行另一個迴圈。

loop }

這個時候透過 Go 讓其中一個函式同步執行，如此就不需要等待該函式執行完後才能執行下一個函式。

func main {

// 透過 `go`，我們可以把這個函式同步執行，

// 如此一來這個函式就不會阻塞主程式的執行。

go loop

// 執行另一個迴圈。

loop }

Goroutine 是類似執行緒的概念，屬於纖程 (區別於協程和執行緒)。執行緒屬於系統層面，通常來說建立一個新的執行緒會消耗較多的資源且管理不易；而協程的主要作用是提供在一個執行緒內的併發性，卻不能利用多個處理器執行緒。而 Goroutine 就像輕量級的執行緒，一個 Go 程式可以執行超過數萬個 Goroutine，並且這些效能都是原生級的，隨時都能夠關閉、結束，且執行在多個處理器執行緒上。一個核心裡面可以有多個 Goroutine，透過 GOMAXPROCS 引數你能夠限制 Gorotuine 可以佔用幾個系統執行緒來避免失控。

在內建的官方套件中也不時能夠看見 Goroutine 的應用，像是 net/http 中用來監聽網路服務的函式實際上是建立一個不斷執行迴圈的 Goroutine；同時搭配了 epoll 等 IO 多路複用機制維護 Goroutine 的事件迴圈。

編譯器

當前有兩個 Go 編譯器分支，分別為官方編譯器 gc 和 gccgo。官方編譯器在初期使用 C 寫成，後用 Go 重寫從而實現自舉。Gccgo 是一個使用標準 GCC 作為後端的 Go 編譯器。

官方編譯器支援跨平臺編譯（但不支援 CGO），允許將原始碼編譯為可在目標系統、架構上執行的二進位制檔案。

應用

由於 go 的原生跨平臺，以及大量的官方庫，被用於大量開源程式，例如：docker、Syncthing、ipfs、Hugo、caddy、以太坊、V2Ray、Gitea、TiDB。