Go創(chuàng)建一個包并使用(導入本地包和注意事項)
Go 語言中,包(Package)的目的和其他語言中的庫或模塊是一樣的,支持模塊化、封裝、單獨編譯和重用。 ——《The Go Programming Language》
有時候需要自己寫一個包方便多次使用,但是在導入自己寫的包時遇到了問題。我以前以為import
部分直接就是包的路徑,但是實際自己寫了之后發(fā)現(xiàn)不是這樣的。這部分實際上這部分是可以解釋成一個標識符,是由一個go.mod
文件確定,一般含義確實是路徑末端。
Go 中模塊的概念其實還包含了一部分版本管理的功能。所以 Go 的模塊和版本管理無論是學習還是開發(fā)都不是一件容易的事情,Go 團隊也在一直努力調整和優(yōu)化。本文只能代表當前版本go1.20.2
版本的情況,如果未來更新了我會進行備注。
go.mod是什么
每個 Go 的模塊都是由go.mod
確定,該文件描述了模塊的屬性,例如模塊存放的路徑是否依賴其他模塊、最低使用 Go 版本等信息。比如mod@v0.8.0
的go.mod
內容為:
module golang.org/x/mod go 1.17 require golang.org/x/tools v0.1.12 // tagx:ignore
然后在編譯的時候,編譯器會去找有沒有這個標識這個模塊的go.mod
,如果有的話找到對應的xxx.go
,然后導入相應的包中使用的功能進行編譯。
這里有兩個問題:
- Go 在哪找模塊的?
- 如何讓 Go 從特定目錄下搜索包?
模塊(module)和包(package)的區(qū)別在于:模塊是一系列包的集合,并且在模塊文件結構的根目錄下有個
go.mod
文件,自己甚至可以直接被編譯成一個程序。而包是某一個或多個.go
文件,用來劃分包級別的作用域(package level),可以當做其他語言的庫。范圍應該是:模塊>>>包>>>源代碼文件。但是在某些情況下,包、模塊、庫這三個詞是可以混用的(在不同情況下叫法不同,但是卻指同一個東西)。
需要注意package main
是個例外,這并不是一個庫(盡管開頭有個package
),而是用來表示這是個可以單獨執(zhí)行的程序。
創(chuàng)建模塊和編寫包的內容
這里舉個例子來進行演示,演示的例子來自《The Go Programming Language》中 Section 2.7,是用來數(shù)一個數(shù)的二進制有多少位為1
,比如輸入1
返回1
,輸入0x1234567890ABCDEF
返回32
。
新建一個文件夾popcount
,然后在里面創(chuàng)建一個名為popcount.go
的文件:
$ mkdir popcount $ cd popcount $ touch popcount.go
輸入以下內容(下面這個算法不是最快的,也不是最容易理解的,但是可以解釋很多東西):
package popcount // pc[i]用來計數(shù)第i位是不是 var pc [256]byte //初始化包 func init() { for i := range pc { pc[i] = pc[i/2] + byte(i&1) } } // PopCount返回x有多少位為1. func PopCount(x uint64) int { return int(pc[byte(x>>(0*8))] + pc[byte(x>>(1*8))] + pc[byte(x>>(2*8))] + pc[byte(x>>(3*8))] + pc[byte(x>>(4*8))] + pc[byte(x>>(5*8))] + pc[byte(x>>(6*8))] + pc[byte(x>>(7*8))]) }
上文中:pc
首字母是小寫的,所以只能在popcount
包中使用,而PopCount
首字母是大寫的,所以可以在導入popcount
包的文件中使用。
繼續(xù)在popcount
中通過go mod init
命令創(chuàng)建go.mod
文件,如下:
$ go mod init test/popcount go: creating new go.mod: module test/popcount go: to add module requirements and sums: go mod tidy $ ls go.mod popcount.go
可以看到多了個go.mod
文件。
導入自建包(本地包)
然后在其他地方新建一個目錄pop_test
來編寫使用這個包的程序代碼(可以和popcount
在同一個目錄下,或者其他地方都行),這里選擇和popcount
在同一個目錄下,如下:
$ cd .. $ mkdir pop_test
然后在pop_test
中新建一個go.mod
:
$ go mod init pop_test
這時候go.mod
的內容應該是如下樣式:
module pop_test go 1.20
用你喜歡的文本編輯器打開它,在末尾添加這樣兩句話,變成如下樣式:
module pop_test go 1.20 require test/popcount v0.0.0 replace test/popcount => ../popcount
最后這兩句都不能省略,少一句都不行。
第一句是為了說明使用popcount
的版本,第二句是因為我們使用的是本地包(local package),而不是下載導入的庫,本地包的位置并不在GOROOT/src/test/popcount
中,Go 編譯的時候找不到的(關于GOROOT
后面還有一些內容)。第二句話其實類似于 C 編譯器中的選項-I
。(這里解決了開頭的那兩個問題)
然后新建一個main.go
文件,輸入以下內容:
package main import ( "fmt" "test/popcount" ) func main() { a := popcount.PopCount(0x1234567890ABCDEF) fmt.Println(a) }
這時候運行應該看到以下結果:
$ go run main.go
32
這種方法是官方推薦的,但是問題在于要在項目的根目錄(如上的pop_test
)下創(chuàng)建一個go.mod
。
第二種方法
下面這種方法是根據(jù)運行機制進行設置的,說實話并不是很方便管理,但是某些情況下卻挺方便的。
上文中提到:Go 默認是在GOROOT/src
下尋找包的,某個包就是GOROOT/src/包名
。那么就可以直接在GOROOT/src
下按照包名的結構放置自建的本地包,然后就可以在程序代碼中直接使用了,不用再在項目根目錄下創(chuàng)建一個go.mod
文件來說明使用的本地包的位置了。
通過以下命令找到你的GOROOT
,如下:
$ go env GOROOT /usr/local/go
你的可能不是/usr/local/go
。對于 Go 的這些環(huán)境變量最好使用go env
查看,如果你使用echo $GOROOT
可能會發(fā)現(xiàn)這個環(huán)境變量是空的。
此外,最好不要用expert
在 Shell 配置文件中修改這個環(huán)境變量,因為標準庫都在默認的GOROOT
中,一旦你切換了,那么這些標準庫你最好都復制到新位置。特殊情況下直接用expert
修改,但是只在當前終端切換,不要徹底替換。
這種方法的最大弊端在于修改了/usr/local/go
,這些默認目錄大部分時期是通過腳本自動操作配置的,如果你進行了修改,那么未來可能會出現(xiàn)問題和沖突,而你又忘了修改了這部分,那就是個很大的問題了。
所以如果必須用這種方法,最好創(chuàng)建一個不會重名(或者概率不大)的文件夾,比如ZhongUncle
,然后在里面創(chuàng)建包和配置go.mod
。
到此這篇關于Go創(chuàng)建一個包并使用(導入本地包和注意事項)的文章就介紹到這了,更多相關Go創(chuàng)建包并導入內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go語言實現(xiàn)基于websocket瀏覽器通知功能
這篇文章主要介紹了Go語言實現(xiàn)基于websocket瀏覽器通知功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07Golang中 import cycle not allowed 問題
這篇文章主要介紹了Golang中 import cycle not allowed 問題的解決方法,問題從描述到解決都非常詳細,需要的小伙伴可以參考一下2022-03-03