Go創(chuàng)建一個(gè)包并使用(導(dǎo)入本地包和注意事項(xiàng))
Go 語(yǔ)言中,包(Package)的目的和其他語(yǔ)言中的庫(kù)或模塊是一樣的,支持模塊化、封裝、單獨(dú)編譯和重用。 ——《The Go Programming Language》
有時(shí)候需要自己寫(xiě)一個(gè)包方便多次使用,但是在導(dǎo)入自己寫(xiě)的包時(shí)遇到了問(wèn)題。我以前以為import部分直接就是包的路徑,但是實(shí)際自己寫(xiě)了之后發(fā)現(xiàn)不是這樣的。這部分實(shí)際上這部分是可以解釋成一個(gè)標(biāo)識(shí)符,是由一個(gè)go.mod文件確定,一般含義確實(shí)是路徑末端。
Go 中模塊的概念其實(shí)還包含了一部分版本管理的功能。所以 Go 的模塊和版本管理無(wú)論是學(xué)習(xí)還是開(kāi)發(fā)都不是一件容易的事情,Go 團(tuán)隊(duì)也在一直努力調(diào)整和優(yōu)化。本文只能代表當(dāng)前版本go1.20.2版本的情況,如果未來(lái)更新了我會(huì)進(jìn)行備注。
go.mod是什么
每個(gè) Go 的模塊都是由go.mod確定,該文件描述了模塊的屬性,例如模塊存放的路徑是否依賴其他模塊、最低使用 Go 版本等信息。比如mod@v0.8.0的go.mod內(nèi)容為:
module golang.org/x/mod go 1.17 require golang.org/x/tools v0.1.12 // tagx:ignore
然后在編譯的時(shí)候,編譯器會(huì)去找有沒(méi)有這個(gè)標(biāo)識(shí)這個(gè)模塊的go.mod,如果有的話找到對(duì)應(yīng)的xxx.go,然后導(dǎo)入相應(yīng)的包中使用的功能進(jìn)行編譯。
這里有兩個(gè)問(wèn)題:
- Go 在哪找模塊的?
- 如何讓 Go 從特定目錄下搜索包?
模塊(module)和包(package)的區(qū)別在于:模塊是一系列包的集合,并且在模塊文件結(jié)構(gòu)的根目錄下有個(gè)
go.mod文件,自己甚至可以直接被編譯成一個(gè)程序。而包是某一個(gè)或多個(gè).go文件,用來(lái)劃分包級(jí)別的作用域(package level),可以當(dāng)做其他語(yǔ)言的庫(kù)。范圍應(yīng)該是:模塊>>>包>>>源代碼文件。但是在某些情況下,包、模塊、庫(kù)這三個(gè)詞是可以混用的(在不同情況下叫法不同,但是卻指同一個(gè)東西)。
需要注意package main是個(gè)例外,這并不是一個(gè)庫(kù)(盡管開(kāi)頭有個(gè)package),而是用來(lái)表示這是個(gè)可以單獨(dú)執(zhí)行的程序。
創(chuàng)建模塊和編寫(xiě)包的內(nèi)容
這里舉個(gè)例子來(lái)進(jìn)行演示,演示的例子來(lái)自《The Go Programming Language》中 Section 2.7,是用來(lái)數(shù)一個(gè)數(shù)的二進(jìn)制有多少位為1,比如輸入1返回1,輸入0x1234567890ABCDEF返回32。
新建一個(gè)文件夾popcount,然后在里面創(chuàng)建一個(gè)名為popcount.go的文件:
$ mkdir popcount $ cd popcount $ touch popcount.go
輸入以下內(nèi)容(下面這個(gè)算法不是最快的,也不是最容易理解的,但是可以解釋很多東西):
package popcount
// pc[i]用來(lái)計(jì)數(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首字母是小寫(xiě)的,所以只能在popcount包中使用,而PopCount首字母是大寫(xiě)的,所以可以在導(dǎo)入popcount包的文件中使用。
繼續(xù)在popcount中通過(guò)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
可以看到多了個(gè)go.mod文件。
導(dǎo)入自建包(本地包)
然后在其他地方新建一個(gè)目錄pop_test來(lái)編寫(xiě)使用這個(gè)包的程序代碼(可以和popcount在同一個(gè)目錄下,或者其他地方都行),這里選擇和popcount在同一個(gè)目錄下,如下:
$ cd .. $ mkdir pop_test
然后在pop_test中新建一個(gè)go.mod:
$ go mod init pop_test
這時(shí)候go.mod的內(nèi)容應(yīng)該是如下樣式:
module pop_test go 1.20
用你喜歡的文本編輯器打開(kāi)它,在末尾添加這樣兩句話,變成如下樣式:
module pop_test go 1.20 require test/popcount v0.0.0 replace test/popcount => ../popcount
最后這兩句都不能省略,少一句都不行。
第一句是為了說(shuō)明使用popcount的版本,第二句是因?yàn)槲覀兪褂玫氖潜镜匕╨ocal package),而不是下載導(dǎo)入的庫(kù),本地包的位置并不在GOROOT/src/test/popcount中,Go 編譯的時(shí)候找不到的(關(guān)于GOROOT后面還有一些內(nèi)容)。第二句話其實(shí)類似于 C 編譯器中的選項(xiàng)-I。(這里解決了開(kāi)頭的那兩個(gè)問(wèn)題)
然后新建一個(gè)main.go文件,輸入以下內(nèi)容:
package main
import (
"fmt"
"test/popcount"
)
func main() {
a := popcount.PopCount(0x1234567890ABCDEF)
fmt.Println(a)
}
這時(shí)候運(yùn)行應(yīng)該看到以下結(jié)果:
$ go run main.go
32
這種方法是官方推薦的,但是問(wèn)題在于要在項(xiàng)目的根目錄(如上的pop_test)下創(chuàng)建一個(gè)go.mod。
第二種方法
下面這種方法是根據(jù)運(yùn)行機(jī)制進(jìn)行設(shè)置的,說(shuō)實(shí)話并不是很方便管理,但是某些情況下卻挺方便的。
上文中提到:Go 默認(rèn)是在GOROOT/src下尋找包的,某個(gè)包就是GOROOT/src/包名。那么就可以直接在GOROOT/src下按照包名的結(jié)構(gòu)放置自建的本地包,然后就可以在程序代碼中直接使用了,不用再在項(xiàng)目根目錄下創(chuàng)建一個(gè)go.mod文件來(lái)說(shuō)明使用的本地包的位置了。
通過(guò)以下命令找到你的GOROOT,如下:
$ go env GOROOT /usr/local/go
你的可能不是/usr/local/go。對(duì)于 Go 的這些環(huán)境變量最好使用go env查看,如果你使用echo $GOROOT可能會(huì)發(fā)現(xiàn)這個(gè)環(huán)境變量是空的。
此外,最好不要用expert在 Shell 配置文件中修改這個(gè)環(huán)境變量,因?yàn)闃?biāo)準(zhǔn)庫(kù)都在默認(rèn)的GOROOT中,一旦你切換了,那么這些標(biāo)準(zhǔn)庫(kù)你最好都復(fù)制到新位置。特殊情況下直接用expert修改,但是只在當(dāng)前終端切換,不要徹底替換。
這種方法的最大弊端在于修改了/usr/local/go,這些默認(rèn)目錄大部分時(shí)期是通過(guò)腳本自動(dòng)操作配置的,如果你進(jìn)行了修改,那么未來(lái)可能會(huì)出現(xiàn)問(wèn)題和沖突,而你又忘了修改了這部分,那就是個(gè)很大的問(wèn)題了。
所以如果必須用這種方法,最好創(chuàng)建一個(gè)不會(huì)重名(或者概率不大)的文件夾,比如ZhongUncle,然后在里面創(chuàng)建包和配置go.mod。
到此這篇關(guān)于Go創(chuàng)建一個(gè)包并使用(導(dǎo)入本地包和注意事項(xiàng))的文章就介紹到這了,更多相關(guān)Go創(chuàng)建包并導(dǎo)入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語(yǔ)言寫(xiě)的簡(jiǎn)要數(shù)據(jù)同步工具詳解
作為go-etl工具的作者,想要安利一下這個(gè)小巧的數(shù)據(jù)同步工具,它在同步百萬(wàn)級(jí)別的數(shù)據(jù)時(shí)表現(xiàn)極為優(yōu)異,基本能在幾分鐘完成數(shù)據(jù)同步,這篇文章主要介紹了go語(yǔ)言寫(xiě)的簡(jiǎn)要數(shù)據(jù)同步工具,需要的朋友可以參考下2024-07-07
Go語(yǔ)言實(shí)現(xiàn)基于websocket瀏覽器通知功能
這篇文章主要介紹了Go語(yǔ)言實(shí)現(xiàn)基于websocket瀏覽器通知功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
Golang中 import cycle not allowed 問(wèn)題
這篇文章主要介紹了Golang中 import cycle not allowed 問(wèn)題的解決方法,問(wèn)題從描述到解決都非常詳細(xì),需要的小伙伴可以參考一下2022-03-03
使用Go實(shí)現(xiàn)郵箱驗(yàn)證碼API功能
本文將帶你了解一個(gè)項(xiàng)目如何實(shí)現(xiàn)一個(gè)郵箱驗(yàn)證接口,即一個(gè)可用的發(fā)送郵箱驗(yàn)證碼API和驗(yàn)證驗(yàn)證碼是否正確功能,對(duì)Go實(shí)現(xiàn)郵箱驗(yàn)證碼API詳細(xì)過(guò)程感興趣的朋友一起看看吧2024-06-06
go?micro微服務(wù)框架項(xiàng)目搭建方法
這篇文章主要為大家介紹了go?micro微服務(wù)框架項(xiàng)目搭建方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
利用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單Ping過(guò)程的方法
相信利用各種語(yǔ)言實(shí)現(xiàn)Ping已經(jīng)是大家喜聞樂(lè)見(jiàn)的事情了,網(wǎng)絡(luò)上利用Golang實(shí)現(xiàn)Ping已經(jīng)有比較詳細(xì)的代碼示例,但大多是僅僅是實(shí)現(xiàn)了Request過(guò)程,而對(duì)Response的回顯內(nèi)容并沒(méi)有做接收。而Ping程序不僅僅是發(fā)送一個(gè)ICMP,更重要的是如何接收并進(jìn)行統(tǒng)計(jì)。2016-09-09
go module構(gòu)建項(xiàng)目的實(shí)現(xiàn)
本文主要介紹了go module構(gòu)建項(xiàng)目的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03

