【Go-bug】go引入自建包名报错 package XXX is not in std
引入
这个问题从昨晚学到Package和Module这块的时候就开始折磨我,这里说道:
Go语言也有 Public 和 Private 的概念,粒度是包。如果类型/接口/方法/函数/字段的首字母大写,则是 Public 的,对其他 package 可见,如果首字母小写,则是 Private 的,对其他 package 不可见。
于是我就打算尝试一下运行结果。最开始我是直接在同一个文件夹下尝试是否能够导入不同go文件的函数,目录结构如下:
D:\Golang\Gowork\src\
├── spider/
├── go.mod
└── main.go
└── add.go
然后发现 add.go
里面小写的函数也能识别,后来往上翻翻发现是这样还是属于一个 Package:
一般来说,一个文件夹可以作为 package,同一个 package 内部变量、类型、方法等定义可以相互看到。比如我们新建一个文件 calc.go, main.go 平级,分别定义 add 和 main 方法。使用
go run .
就能互相使用对方的函数
问题描述
于是我开始尝试跨包导入以验证他的私有属性,目录结构如下:
D:\Golang\Gowork\src\
├── spider/
│ ├── go.mod
│ └── main.go
└── train/
├── go.mod
└── train.go
其中 train.go
:
package train
func mul (a int, b int) (int) {
return a * b
}
func Mul (a int, b int) (int) {
return a * b
}
func Add (a int, b int) (int) {
return a + b
}
func add (a int, b int) (int) {
return a + b
}
main.go
:
// +build ignore
package main
import (
"fmt"
"train"
)
func main() {
fmt.Printf("add_ans:%d\n", train.Mul(1, 2))
fmt.Printf("add_ans:%d\n", train.Add(1, 2))
// fmt.Printf("add_ans:%d\n", train.add(1, 2))
// fmt.Printf("add_ans:%d\n", train.mul(1, 2))
}
spider/go.mod
:
module spider
go 1.23.6
train/go.mod
:
module train
go 1.23.6
然后在spider目录下 go run .
,结果显示了这个报错:
PS D:\Golang\Gowork\src\spider> go run .
main.go:7:2: package train is not in std (D:\Golang\GoInstall\src\train)
我发现这个路径定位的有问题,我明明是在Gowork,他给我找到GoInstall里面去了,于是我就搜寻了一下,找到的都说是GOPATH可能出了问题,我就去环境变量上都加上了:
Path也加了(用户和系统都有):
在终端中查看环境:
发现GOPATH确实没问题,然而他还是回到GoInstall里面找,我都没法理解。去StackOverflow上搜了同样的问题,那个人说改module里面的名字,然后我改了也不行。找了好多篇都是差不多的说法。
解决
后面终于看到csdn上一篇说到了环境的另一个地方的问题:
其实问题就出在我这个Go111MODULE什么也没设置上,因此只能找到标准库的东西
。按照他的指示将其置为off后,终于可以了,这时就根据正确的GOPATH成功导入了train这个package,测试之后发现,首字母大写的train.Add
和 train.Mul
就可以成功运行,而train.add
和train.mul
是无法识别到的,因为他是私有的:
这样就算成功解决了这个bug
进一步扩展
本来我也没想着再尝试另一种方法,就是问了一下deepseek关于包和模块的概念(回答原文见下),因为有点懵,结果他顺便提供了另一种思路,也就是将Go111MODULE设置为on的解决方法,即模块管理。先打开这个模块:
核心部分是这里:
也就是要让引用的moudle能够成功找到自定义包,因此要加入路径。同时还有运行之前一定要执行go mod tidy # 自动处理依赖关系
来处理依赖(说到这里之前init的时候也有提示,不过我一直以为他是创建了一个类似readme的东西所以一直没理会,导致走了这么多弯路。。。
言归正传,这样子就能够使用多模块的方式来导入自定义包了。
Ref
https://blog.csdn.net/qq_50750196/article/details/134261664
deepseek:
deepseek原文
嗯,用户现在对Go语言中的包(package)和模块(module)之间的关系有些困惑,特别是当项目结构中有多个目录时,如何区分包和模块,以及如何处理它们的从属关系。我需要详细解释这两个概念的区别和联系,并举例说明不同情况下的处理方法。
首先,用户的问题主要围绕以下几点:
- Go中是否根据文件夹来区分包?
- 如果src下有spider和train两个目录,是否属于两个不同的包?
- 模块(module)是什么概念?
- 如果包之间有从属关系,应该如何处理?
我需要逐一解答这些问题,并结合之前的对话内容,确保回答的一致性和准确性。
首先,关于包(package)的概念。在Go中,每个文件夹对应一个包,同一个文件夹下的所有Go文件必须声明同一个包名。包名通常与文件夹名一致,但也可以不同。不过按照惯例,最好保持一致,避免混淆。因此,如果用户有spider和train两个文件夹,且每个文件夹下的Go文件都声明了对应的包名,那么它们确实是两个不同的包。
接下来是模块(module)。模块是Go 1.11引入的概念,用于管理项目的依赖和版本。模块是一个包含go.mod文件的目录树,它定义了模块的路径(通常是仓库的URL,如github.com/user/project)及其依赖。模块可以包含多个包,这些包可以是模块内部的子目录,也可以是其他模块的依赖。
用户提到的src目录结构可能涉及到GOPATH模式,但自从引入模块后,GOPATH的作用减弱了。在模块模式下,项目不需要放在GOPATH/src下,但用户可能仍然在使用旧的结构。需要解释模块和GOPATH的关系,以及如何正确配置。
关于包之间的从属关系,如果spider和train属于同一个模块,那么可以直接通过导入路径引用,如模块名/子目录。如果它们是不同的模块,则需要通过go.mod中的require和replace指令来管理依赖,特别是当train是本地开发的一个模块时,需要用replace来指向本地路径。
用户可能还混淆了包和模块的概念,需要明确区分:包是代码组织的单位,而模块是依赖管理的单位。一个模块可以包含多个包,这些包通过导入路径访问。模块路径决定了包的导入路径前缀。
还需要考虑用户的实际项目结构,假设他们的GOPATH设置为D:\Golang\Gowork,那么src下的spider和train可能分别对应不同的包,但如果没有使用模块,可能会遇到导入问题。建议用户迁移到模块模式,这样可以更灵活地管理依赖,避免路径问题。
最后,用户可能需要具体的示例来说明不同情况下的go.mod配置,以及如何正确导入包。例如,当spider和train属于同一模块时,如何导入;当它们是不同模块时,如何配置replace指令。同时,要提醒用户注意模块名的唯一性,通常使用仓库URL格式,即使代码不上传到远程仓库,这有助于避免路径冲突。
总结来说,我需要逐步解释包和模块的概念,它们的区别,如何根据项目结构配置go.mod文件,以及如何处理包之间的依赖关系,同时结合用户的实际目录结构和之前的建议,提供具体的配置示例和操作步骤,确保用户能够理解并正确应用这些概念。
正文
在 Go 语言中,包(Package)和模块(Module)是两个不同但相关的概念。以下是详细的解释和实际场景的说明:
一、包(Package)的规则
1. 包与目录的关系
- 一个目录对应一个包:Go 语言中,同一个目录下的所有
.go
文件必须属于同一个包。 - 包名约定:通常包名与目录名保持一致(例如目录
train
下的文件声明为package train
),但也可以不同(不推荐,容易造成混淆)。
2. 包的导入
- 假设你的项目结构如下(使用传统
GOPATH
模式):D:\Golang\Gowork\src\ ├── spider/ │ └── main.go └── train/ └── train.go
- 在
main.go
中导入train
包:import "train" // 基于 GOPATH 的绝对路径
- 此时:
spider
和train
是两个独立的包,彼此没有层级关系。
二、模块(Module)的概念
1. 模块是什么?
- 模块是更高一级的代码单元:模块是 Go 1.11 引入的依赖管理机制,一个模块对应一个
go.mod
文件,用于定义代码的依赖关系。 - 模块路径:通常是代码仓库的路径(如
github.com/yourname/spider
),保证全局唯一性。 - 模块包含包:一个模块可以包含多个包,这些包通过模块路径前缀访问(如
github.com/yourname/spider/train
)。
2. 模块与包的关系
- 单模块项目:如果
spider
和train
属于同一模块,则train
是spider
的子包。 - 多模块项目:如果
spider
和train
是两个独立模块,则它们是两个完全独立的代码单元,通过模块依赖机制关联。
三、不同场景下的项目结构
场景 1:单模块项目(推荐)
结构:
D:\Golang\Gowork\src\spider\
├── go.mod // 模块名:spider(或更规范的 github.com/yourname/spider)
├── main.go // 主程序
└── train/
└── train.go // 包声明为 package train
关键点:
- 所有代码属于一个模块(
spider
),train
是模块内的子包。 - 导入方式:
import "spider/train" // 模块名 + 子包路径
go.mod
内容:module spider // 建议改为全局唯一路径(如 github.com/yourname/spider) go 1.21
场景 2:多模块项目(高级用法)
结构:
D:\Golang\Gowork\src\
├── spider/
│ ├── go.mod // 模块名:spider
│ └── main.go
└── train/
├── go.mod // 模块名:train
└── train.go
关键点:
spider
和train
是两个独立模块。spider
的go.mod
:module spider go 1.21 require train v0.0.0 replace train => ../train // 本地路径替换
train
的go.mod
:module train go 1.21
- 导入方式:
import "train" // 直接使用模块名
四、包与模块的从属关系
1. 同一模块内的子包
- 层级关系:通过目录结构体现(如
spider/train
)。 - 导入路径:
模块名/子包路径
(如spider/train
)。
2. 不同模块的依赖
- 层级关系:通过
go.mod
的require
和replace
指令管理。 - 导入路径:直接使用模块名(如
train
)。
五、操作步骤与验证
1. 初始化模块
- 单模块项目:
cd D:\Golang\Gowork\src\spider go mod init spider
- 多模块项目:
# 初始化 train 模块 cd D:\Golang\Gowork\src\train go mod init train # 初始化 spider 模块 cd D:\Golang\Gowork\src\spider go mod init spider
2. 解决依赖
go mod tidy # 自动处理依赖关系
3. 运行程序
go run .
六、常见问题
1. 错误:package train is not in std
- 原因:Go 编译器在
GOROOT
和GOPATH
中找不到train
包。 - 解决:
- 确保
train
包路径正确(在GOPATH/src
或模块路径内)。 - 检查
go.mod
中的模块配置和replace
指令。
- 确保
2. 错误:cannot find module providing package train
- 原因:未正确声明模块依赖。
- 解决:
- 在
go.mod
中添加require train
并指定版本(或使用replace
指向本地路径)。
- 在
七、总结
- 包(Package):代码组织的原子单位,与目录一一对应。
- 模块(Module):依赖管理的核心单元,通过
go.mod
定义。 - 从属关系:
- 同一模块内的包:通过目录层级和模块路径导入。
- 不同模块的包:通过
require
和replace
管理依赖。
优先使用单模块项目简化管理,只有在需要复用代码为独立库时才拆分为多模块!