Go创建一个包并使用(导入本地包和注意事项)

 更新时间:2023年11月06日 09:44:01   作者:zhonguncle  
有时候需要自己写一个包方便多次使用,但是在导入自己写的包时遇到了问题,本文主要介绍了Go创建一个包并使用(导入本地包和注意事项),感兴趣的可以了解一下

Go 语言中,包(Package)的目的和其他语言中的库或模块是一样的,支持模块化、封装、单独编译和重用。 ——《The Go Programming Language》

有时候需要自己写一个包方便多次使用,但是在导入自己写的包时遇到了问题。我以前以为import部分直接就是包的路径,但是实际自己写了之后发现不是这样的。这部分实际上这部分是可以解释成一个标识符,是由一个go.mod文件确定,一般含义确实是路径末端。

Go 中模块的概念其实还包含了一部分版本管理的功能。所以 Go 的模块和版本管理无论是学习还是开发都不是一件容易的事情,Go 团队也在一直努力调整和优化。本文只能代表当前版本go1.20.2版本的情况,如果未来更新了我会进行备注。

go.mod是什么

每个 Go 的模块都是由go.mod确定,该文件描述了模块的属性,例如模块存放的路径是否依赖其他模块、最低使用 Go 版本等信息。比如mod@v0.8.0go.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)的区别在于:模块是一系列包的集合,并且在模块文件结构的根目录下有个go.mod文件,自己甚至可以直接被编译成一个程序。而包是某一个或多个.go文件,用来划分包级别的作用域(package level),可以当做其他语言的库。范围应该是:模块>>>包>>>源代码文件。但是在某些情况下,包、模块、库这三个词是可以混用的(在不同情况下叫法不同,但是却指同一个东西)。
需要注意package main是个例外,这并不是一个库(尽管开头有个package),而是用来表示这是个可以单独执行的程序。

创建模块和编写包的内容

这里举个例子来进行演示,演示的例子来自《The Go Programming Language》中 Section 2.7,是用来数一个数的二进制有多少位为1,比如输入1返回1,输入0x1234567890ABCDEF返回32

新建一个文件夹popcount,然后在里面创建一个名为popcount.go的文件:

$ mkdir popcount
$ cd popcount
$ touch popcount.go

输入以下内容(下面这个算法不是最快的,也不是最容易理解的,但是可以解释很多东西):

package popcount

// pc[i]用来计数第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包的文件中使用。

继续在popcount中通过go mod init命令创建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)下创建一个go.mod

第二种方法

下面这种方法是根据运行机制进行设置的,说实话并不是很方便管理,但是某些情况下却挺方便的。

上文中提到:Go 默认是在GOROOT/src下寻找包的,某个包就是GOROOT/src/包名。那么就可以直接在GOROOT/src下按照包名的结构放置自建的本地包,然后就可以在程序代码中直接使用了,不用再在项目根目录下创建一个go.mod文件来说明使用的本地包的位置了。

通过以下命令找到你的GOROOT,如下:

$ go env GOROOT
/usr/local/go

你的可能不是/usr/local/go。对于 Go 的这些环境变量最好使用go env查看,如果你使用echo $GOROOT可能会发现这个环境变量是空的。

此外,最好不要用expert在 Shell 配置文件中修改这个环境变量,因为标准库都在默认的GOROOT中,一旦你切换了,那么这些标准库你最好都复制到新位置。特殊情况下直接用expert修改,但是只在当前终端切换,不要彻底替换。

这种方法的最大弊端在于修改了/usr/local/go,这些默认目录大部分时期是通过脚本自动操作配置的,如果你进行了修改,那么未来可能会出现问题和冲突,而你又忘了修改了这部分,那就是个很大的问题了。

所以如果必须用这种方法,最好创建一个不会重名(或者概率不大)的文件夹,比如ZhongUncle,然后在里面创建包和配置go.mod

到此这篇关于Go创建一个包并使用(导入本地包和注意事项)的文章就介绍到这了,更多相关Go创建包并导入内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言LeetCode题解1046最后一块石头的重量

    Go语言LeetCode题解1046最后一块石头的重量

    这篇文章主要为大家介绍了Go语言LeetCode题解1046最后一块石头的重量,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • golang开发微框架Gin的安装测试及简介

    golang开发微框架Gin的安装测试及简介

    这篇文章主要为大家介绍了golang微框架Gin的安装测试及简介,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2021-11-11
  • golang包循环引用的几种解决方案总结

    golang包循环引用的几种解决方案总结

    golang有包循环引用问题,用过的应该都知道,下面这篇文章主要给大家介绍了关于golang包循环引用的几种解决方案,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • Go语言并发范式之future模式详解

    Go语言并发范式之future模式详解

    编程中经常遇到在一个流程中需要调用多个子调用的情况,此时就可以使用Go并发编程中的future模式,下面小编就来和大家聊聊future模式的具体使用,需要的可以参考一下
    2023-06-06
  • Golang Defer作用域及执行顺序使用案例

    Golang Defer作用域及执行顺序使用案例

    这篇文章主要为大家介绍了Golang Defer作用域及执行顺序使用案例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • go sync包中的互斥锁Mutex和等待组WaitGroup使用详解

    go sync包中的互斥锁Mutex和等待组WaitGroup使用详解

    这篇文章主要为大家介绍了go sync包中的互斥锁Mutex和等待组WaitGroup使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Go语言实现Fibonacci数列的方法

    Go语言实现Fibonacci数列的方法

    这篇文章主要介绍了Go语言实现Fibonacci数列的方法,实例分析了使用递归和不使用递归两种技巧,并对算法的效率进行了对比,需要的朋友可以参考下
    2015-02-02
  • golang gorm的预加载及软删硬删的数据操作示例

    golang gorm的预加载及软删硬删的数据操作示例

    这篇文章主要介绍了golang gorm的预加载及软删硬删的数据操作示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Golang实现IO操作

    Golang实现IO操作

    本文主要介绍了Golang实现IO操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-05-05
  • 教你一招完美解决vscode安装go插件失败问题

    教你一招完美解决vscode安装go插件失败问题

    VSCode是我们开发go程序的常用工具,但是安装VSCode成功后,创建一个.go文件居然提示错误了,所以下面下面这篇文章主要给大家介绍了如何通过一招完美解决vscode安装go插件失败问题的相关资料,需要的朋友可以参考下
    2022-07-07

最新评论