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語言不一樣,他們的區別絕對不是include
和 import
不同寫法的區別。
首先展示一下我把import
當include
用遇到的問題
檔案目錄:
.
├── 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