本章節涵蓋 Go 語言的基礎概念,包括工作區組態、原始碼組織、程式實體以及模組管理。

工作區與 GOPATH#

GOPATH 的角色#

GOPATH 是 Go 語言的工作區路徑,它決定了 Go 語言原始碼、編譯產物和可執行檔案的存放位置。

GOPATH 的值不能與 GOROOT(Go 安裝目錄)相同。GOPATH 可以包含多個路徑,在類 Unix 系統中使用冒號分隔,在 Windows 系統中使用分號分隔。

工作區目錄結構#

GOPATH/
├── src/    # 原始碼目錄
├── pkg/    # 編譯後的套件歸檔檔案 (.a)
└── bin/    # 可執行檔案

src 目錄:存放所有 Go 原始碼檔案。原始碼的組織方式是以程式碼套件為基本單位,一個程式碼套件對應 src 目錄下的一個目錄。

pkg 目錄:存放編譯後的套件歸檔檔案(.a 檔案)。這些檔案按照平台架構組織,例如 linux_amd64darwin_arm64 等。

bin 目錄:存放編譯後的可執行檔案。可以將此目錄加入系統 PATH 環境變數,方便直接執行命令。

套件匯入路徑範例

假設 GOPATH 為 /home/user/go,則:

/home/user/go/
└── src/
    └── github.com/
        └── username/
            └── myproject/
                ├── main.go
                └── utils/
                    └── helper.go
  • myproject 的匯入路徑是 github.com/username/myproject
  • utils 套件的匯入路徑是 github.com/username/myproject/utils

原始碼檔案分類#

Go 語言的原始碼檔案分為三種:命令源碼檔案、程式庫源碼檔案和測試源碼檔案。

命令源碼檔案#

命令源碼檔案是 Go 程式的入口點,具有以下特徵:

  • 必須屬於 main 套件(package main
  • 必須包含無參數、無回傳值的 main 函式
package main

import "fmt"

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

同一個程式碼套件中不能同時存在多個命令源碼檔案。如果違反此規則,執行 go buildgo run 時會報錯。

接收命令列參數#

使用 flag 套件處理命令列參數是 Go 的慣用做法:

package main

import (
    "flag"
    "fmt"
)

var name string

func init() {
    // 定義命令列參數
    flag.StringVar(&name, "name", "World", "使用者名稱")
}

func main() {
    // 解析命令列參數
    flag.Parse()
    fmt.Printf("Hello, %s!\n", name)
}

執行方式:

go run main.go -name="Go"
# 輸出: Hello, Go!
flag 套件常用函式
函式說明
flag.StringVar綁定字串類型參數到變數
flag.IntVar綁定整數類型參數到變數
flag.BoolVar綁定布林類型參數到變數
flag.Parse解析命令列參數
flag.Usage列印使用說明

程式庫源碼檔案#

程式庫源碼檔案用於封裝可重用的程式碼,供其他套件匯入使用。

// 檔案: utils/math.go
package utils

// Add 計算兩個整數的和(公開函式)
func Add(a, b int) int {
    return a + b
}

// multiply 計算兩個整數的積(私有函式)
func multiply(a, b int) int {
    return a * b
}

Go 語言透過識別字首字母的大小寫來控制存取權限:

  • 首字母大寫:公開(可被其他套件存取)
  • 首字母小寫:私有(僅限當前套件存取)

internal 套件機制#

internal 目錄是 Go 的特殊機制,用於限制套件的可見範圍:

project/
├── internal/
│   └── secret/     # 僅 project 及其子套件可存取
│       └── config.go
└── main.go

程式實體#

程式實體是 Go 語言中可以被命名的元素,包括變數、常數、函式和型別。

變數宣告#

Go 語言提供多種變數宣告方式:

// 標準宣告
var name string = "Go"

// 型別推斷
var version = "1.21"

// 短變數宣告(僅限函式內使用)
count := 10

// 多變數宣告
var (
    width  = 100
    height = 200
)

短變數宣告 := 只能在函式內部使用,但它更加簡潔。在套件層級宣告變數時,必須使用 var 關鍵字。

型別推斷規則#

// 整數字面量預設推斷為 int
x := 42          // int

// 浮點數字面量預設推斷為 float64
y := 3.14        // float64

// 字串字面量推斷為 string
s := "hello"     // string

// 從已有變數推斷
var a int32 = 100
b := a           // int32(繼承 a 的型別)

型別轉換與型別斷言#

Go 是強型別語言,不同型別之間需要顯式轉換:

// 型別轉換(用於基本型別)
var i int = 42
var f float64 = float64(i)

// 型別斷言(用於介面型別)
var val interface{} = "hello"

// 安全的型別斷言
if str, ok := val.(string); ok {
    fmt.Println("字串值:", str)
} else {
    fmt.Println("不是字串型別")
}

// 型別 switch
switch v := val.(type) {
case string:
    fmt.Println("字串:", v)
case int:
    fmt.Println("整數:", v)
default:
    fmt.Println("未知型別")
}

變數重宣告規則#

在短變數宣告中,允許部分變數重宣告:

var err error

// 合法:n 是新變數,err 是重宣告
n, err := fmt.Println("hello")

// 合法:同一區塊中 err 被重新賦值
m, err := fmt.Println("world")

變數重宣告必須滿足以下條件:

  1. 必須有至少一個新變數
  2. 重宣告的變數型別必須與原宣告一致
  3. 必須在同一個程式碼區塊中

程式碼區塊與作用域#

Go 語言有六種程式碼區塊,由小到大依次是:

  1. 語句區塊:if、for、switch 等語句的區塊
  2. 函式區塊:函式內部區域
  3. 套件區塊:當前套件的所有原始碼
  4. 檔案區塊:當前檔案的程式碼
  5. 匯入區塊:匯入的套件範圍
  6. 全域區塊:內建識別字(如 int、string、true 等)
package main

var global = "套件層級變數"

func main() {
    local := "函式層級變數"

    if true {
        inner := "區塊層級變數"
        fmt.Println(inner)  // 可存取
    }
    // fmt.Println(inner)  // 錯誤:inner 在此不可見

    fmt.Println(local, global)
}
變數遮蔽範例

內層區塊的變數會遮蔽外層同名變數:

package main

import "fmt"

var x = "全域"

func main() {
    fmt.Println(x)  // 輸出: 全域

    x := "區域"
    fmt.Println(x)  // 輸出: 區域

    if true {
        x := "內層"
        fmt.Println(x)  // 輸出: 內層
    }

    fmt.Println(x)  // 輸出: 區域
}

Go Modules#

Go Modules 是 Go 語言官方的依賴管理解決方案,從 Go 1.11 開始引入,Go 1.16 後成為預設模式。

初始化模組#

# 建立新模組
go mod init github.com/username/myproject

# 這會建立 go.mod 檔案

go.mod 檔案結構#

module github.com/username/myproject

go 1.21

require (
    github.com/gin-gonic/gin v1.9.1
    golang.org/x/sync v0.3.0
)

require (
    // 間接依賴
    github.com/bytedance/sonic v1.9.1 // indirect
)

常用命令#

# 下載依賴
go mod download

# 整理依賴(移除未使用的,加入缺少的)
go mod tidy

# 檢視依賴關係圖
go mod graph

# 驗證依賴完整性
go mod verify

# 將依賴複製到 vendor 目錄
go mod vendor

建議在專案根目錄執行 go mod tidy 來自動整理依賴。這個命令會移除未使用的依賴,並加入程式碼中引用但 go.mod 中缺少的依賴。

版本選擇規則#

Go Modules 遵循語意化版本(Semantic Versioning)規範:

  • v1.2.3:主版本.次版本.修訂版本
  • 主版本 0 或 1 時,匯入路徑不變
  • 主版本 2 及以上時,匯入路徑需加入版本後綴
import (
    "github.com/user/pkg"      // v0.x.x 或 v1.x.x
    "github.com/user/pkg/v2"   // v2.x.x
    "github.com/user/pkg/v3"   // v3.x.x
)
go.sum 檔案說明

go.sum 檔案記錄所有依賴的加密雜湊值,用於驗證下載的模組是否被篡改:

github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqFPSdI=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL8YrYN+...

每個依賴會有兩行記錄:

  • 第一行:模組內容的雜湊值
  • 第二行:go.mod 檔案的雜湊值