Golang學習筆記(1):包管理

Withm發表於2024-06-06

Golang學習筆記(1):包管理

本人學習Golang主要是為了做MIT6.824的lab,然而一上來就被Golang神奇的import搞混了,因此寫一篇部落格記錄學習Golang的包管理的過程。

package main

import "fmt"

func main() {
    fmt.Println("hello, world")
}

如果有程式設計基礎肯定會覺得這段程式碼很好理解,它可以在終端輸入hello,world並自動換行。但如果深究這段程式碼裡的細節我們可以提出很多有價值的問題。

  • package main 是什麼意思?
  • fmt 也是一個package嗎?
  • 如果把func作為main函式的關鍵詞提示,那麼它返回什麼,傳入什麼?這在Golang裡該如何表示?

本篇文章主要解決關於package的問題,因為這個問題在根本上和博主熟知的C語言不一樣,他們的區別絕對不是includeimport 不同寫法的區別。

首先展示一下我把importinclude用遇到的問題

檔案目錄:

.
├── main.go
└── pkg_va.go

1 directory, 2 files

main.go

package main

import (
        "fmt"
        "mypkg"
)

func main() {
        fmt.Println(mypkg.I)
}

pkg_var.go

package mypkg

var I int

func init() {
        I = 10
}

按我的想法我把mypkg包括進來了就應該能用mypkg相關檔案裡的值了。

然而卻遇到了這樣的問題

xiongzile@LAPTOP-S6FR5EAE:~/workspace/demo/go$ go run main.go
main.go:5:2: package mypkg is not in std (/usr/lib/go-1.22/src/mypkg)

這段報錯說是系統中找不到我的包,如果你對C語言很熟悉你就會想到這是

#include <xxx.h>

而這個xx.h是你自己定義的標頭檔案會出現的錯誤。

那我們有沒有類似

#include "xxx.h"

的表達呢?

很遺憾Golang似乎是沒有的,發現了這一點我就明白了學C的我對包管理還是一竅不通的狀態,所以我要開始學習Golang的包管理了!

早期Golang的包管理很簡單粗暴, 有一個GOPATH環境變數, 系統識別包路徑不僅會識別std裡的,還會識別這個環境變數對應的路徑下的。 這會導致開發者建立專案是不自由的,隨時要改GOPATH環境變數,我認為沒人想每次換路徑都改環境變數。

Golang認識到這一點之後推出了Modules包管理,也就是現在的Go開發者真正需要學習瞭解的,也是這篇部落格的重點。

回到剛才那個問題,我們如何用Go Modules來管理外部庫(我們自己寫的庫或者在網上找的別人寫的庫)的依賴?

首先要在專案根目錄下啟動我們自己的Go模組:

go mod init yourmodulename

效果如下:

xiongzile@LAPTOP-S6FR5EAE:~/workspace/demo/go$ go mod init mymod
go: creating new go.mod: module mymod
go: to add module requirements and sums:
        go mod tidy

這樣我們就有了一個自己的模板,我們可以在這裡自由的匯入或者移除外部庫

修改一下main.go程式碼

package main

import (
        "fmt"
        "mymod/mypkg"
)

func main() {
        fmt.Println(mypkg.I)
}

這樣是不是就可以了呢?

xiongzile@LAPTOP-S6FR5EAE:~/workspace/demo/go$ go run main.go
main.go:5:9: package mymod/mypkg is not in std (/usr/lib/go-1.22/src/mymod/mypkg)

答案是依舊不行,因為Golang規定匯入包必須滿足如下類似目錄結構,請參考:

.
├── go.mod
├── main.go
└── mypkg
    └── pkg_va.go

2 directories, 3 files
xiongzile@LAPTOP-S6FR5EAE:~/workspace/demo/go$ go run main.go
10

現在終於可以啦!我們注意到使用了這個指令之後,我們的工程目錄下多了一個檔案go.mod

我們看一下里麵包括什麼內容:

 module mymod

 go 1.22.2

目前這個檔案只包括了我們的module名和我們使用的go版本號。在我們嘗試使用外部庫的時候會出現更多內容。

接下來看一個程式碼示例:

// main.go
package main
import (
    "github.com/gorilla/mux"
    "log"
    "net/http"
    "os"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("處理首頁請求")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("歡迎來到Go Modules Web服務!"))
}
func main() {
    // 使用環境變數或預設值設定日誌檔案
    logFile, err := os.OpenFile(os.Getenv("LOG_FILE_PATH"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        log.Fatal("開啟日誌檔案失敗:", err)
    }
    defer logFile.Close()
    // 設定日誌輸出
    log.SetOutput(logFile)
    r := mux.NewRouter()
    r.HandleFunc("/", homeHandler)
    log.Println("啟動Web服務:localhost:8080")
    if err := http.ListenAndServe(":8080", r); err != nil {
        log.Fatal("啟動Web服務失敗:", err)
    }
}

我們嘗試執行,得到了這樣的結果:

xiongzile@LAPTOP-S6FR5EAE:~/workspace/demo/go$ go run main.go
main.go:4:5: no required module provides package github.com/gorilla/mux; to add it:
        go get github.com/gorilla/mux

我們按照go的提示輸入go get github.com/gorilla/mux

再次檢視go.mod

module mymod

go 1.22.2

require github.com/gorilla/mux v1.8.1

多了一個require 關鍵詞,結合我們剛才的操作不難理解,這個關鍵詞是為了讓我們獲取外部庫,並能在這個專案中運用。

到目前為止,我們就能夠在我們的專案裡實現對外部庫和我們自己的庫的呼叫了,接下來提供幾個常用的關於go module管理的指令。

go mod init + modulename : 初始化模組,併為當前模組命名。

go mod tidy: 管理包,增加本地缺失的包,刪除沒用的包。

go mod graph: 檢視模組間依賴圖

學到這裡應該就能進行基本的包管理(目前是夠用了),進階的以後再慢慢學。

博主是Golang的初學者,如果有任何錯誤歡迎提醒我。

博主郵箱

xiongzile99@gmail.com

參考資料

https://developer.aliyun.com/article/1486997?spm=5176.26934562.main.6.215e4a0aZRNBnS

相關文章