Go語言

<image>

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

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

在2009年與2016年，Go在TIOBE指數中被列選為最佳語言。

目前，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 的垃圾回收機制一直被人詬病，直到 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時，缺少對泛型編程的支持和冗長的異常處理而備受批評。

### 版本歷史

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

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

Release｜Status｜Release date｜Maintenance end

o｜o｜2012-03-28｜2013-12-01

o｜o｜2013-05-13｜2014-06-18

o｜o｜2013-12-01｜2014-12-10

o｜o｜2014-06-18｜2015-08-19

o｜o｜2014-12-10｜2016-02-17

o｜o｜2015-08-19｜2016-08-15

o｜o｜2016-02-17｜2017-02-16

o｜o｜2016-08-15｜2017-08-24

o｜o｜2017-02-16｜2018-02-16

o｜o｜2017-08-24｜2018-08-24

o｜o｜2018-02-16｜2019-02-25

o｜o｜2018-08-24｜2019-09-03

o｜o｜2019-02-25｜2020-02-25

o｜o｜2019-09-03｜2020-08-11

o｜o｜2020-02-25｜2021-02-16

o｜o｜2020-08-11｜2021-08-16

o｜o｜2021-02-16｜2022-03-15

o｜o｜2021-08-16｜2022-08-02

o｜o｜2022-03-15｜2023-02-01

o｜o｜2022-08-02｜2023-08-08

o｜o｜2023-02-01｜2024-02-06

o｜o｜2023-08-08｜2024-08-13

o｜o｜2024-02-06｜2025-02-11

o｜o｜2024-08-13｜2025-08-12

o｜o｜2025-02-11｜2026-02-04

2025-08-12｜Q3 2026

c｜c｜2026-02-10｜Q1 2027

l

## 代碼示例

### Hello World

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

```go
package main

import "fmt"

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

### HTTP網頁伺服器

透過Go僅需幾行程式碼就完成HTTP網頁伺服器的實現：

```go
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)
}
```

### Echo命令程序

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

```go
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`迴圈不需要以小括號包覆起來。
- 使用 tab 做排版

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

### 專案架構

#### 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 
)
```

#### 工作區

Go的工作區位於`GOPATH`，其目錄結構如下：

```
src
pkg
bin
```

三個目錄的用途分別為

目錄｜用途

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也非常地適合網路服務。假設有個程式，裡面有兩個函式：

```go
func main() {
    // 假設 loop 是一個會重複執行十次的迴圈函式。
    // 迴圈執行完畢才會往下執行。
    loop()
    // 執行另一個迴圈。
    loop()
}
```

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

```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。

## 基本資料

- 日語原文：Go
- 編程范型：編譯型，並行處理，結構化，指令式
- 發行時間：2009年11月10日
- 設計者：羅伯特·格瑞史莫、羅勃·派克、肯·湯普遜
- 開發者：Google
- 軟體實現：gc (8g，6g，5g)，gccgo
- 許可證：BSD
- 網站：https://go.dev/、https://golang.google.cn/
- 文件擴展名：.go
- 受影響於：C、Oberon、Limbo、Newsqueak
- 作業系統：Linux、macOS、FreeBSD、Windows