【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.Addtrain.Mul就可以成功运行,而train.addtrain.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)之间的关系有些困惑,特别是当项目结构中有多个目录时,如何区分包和模块,以及如何处理它们的从属关系。我需要详细解释这两个概念的区别和联系,并举例说明不同情况下的处理方法。

首先,用户的问题主要围绕以下几点:

  1. Go中是否根据文件夹来区分包?
  2. 如果src下有spider和train两个目录,是否属于两个不同的包?
  3. 模块(module)是什么概念?
  4. 如果包之间有从属关系,应该如何处理?

我需要逐一解答这些问题,并结合之前的对话内容,确保回答的一致性和准确性。

首先,关于包(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 的绝对路径
    
  • 此时spidertrain 是两个独立的包,彼此没有层级关系。

二、模块(Module)的概念

1. 模块是什么?

  • 模块是更高一级的代码单元:模块是 Go 1.11 引入的依赖管理机制,一个模块对应一个 go.mod 文件,用于定义代码的依赖关系。
  • 模块路径:通常是代码仓库的路径(如 github.com/yourname/spider),保证全局唯一性。
  • 模块包含包:一个模块可以包含多个包,这些包通过模块路径前缀访问(如 github.com/yourname/spider/train)。

2. 模块与包的关系

  • 单模块项目:如果 spidertrain 属于同一模块,则 trainspider 的子包。
  • 多模块项目:如果 spidertrain 是两个独立模块,则它们是两个完全独立的代码单元,通过模块依赖机制关联。

三、不同场景下的项目结构

场景 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

关键点

  • spidertrain 是两个独立模块。
  • spidergo.mod
    module spider
    go 1.21
    require train v0.0.0
    replace train => ../train  // 本地路径替换
    
  • traingo.mod
    module train
    go 1.21
    
  • 导入方式
    import "train"  // 直接使用模块名
    

四、包与模块的从属关系

1. 同一模块内的子包

  • 层级关系:通过目录结构体现(如 spider/train)。
  • 导入路径模块名/子包路径(如 spider/train)。

2. 不同模块的依赖

  • 层级关系:通过 go.modrequirereplace 指令管理。
  • 导入路径:直接使用模块名(如 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 编译器在 GOROOTGOPATH 中找不到 train 包。
  • 解决
    • 确保 train 包路径正确(在 GOPATH/src 或模块路径内)。
    • 检查 go.mod 中的模块配置和 replace 指令。

2. 错误:cannot find module providing package train

  • 原因:未正确声明模块依赖。
  • 解决
    • go.mod 中添加 require train 并指定版本(或使用 replace 指向本地路径)。

七、总结

  • 包(Package):代码组织的原子单位,与目录一一对应。
  • 模块(Module):依赖管理的核心单元,通过 go.mod 定义。
  • 从属关系
    • 同一模块内的包:通过目录层级和模块路径导入。
    • 不同模块的包:通过 requirereplace 管理依赖。

优先使用单模块项目简化管理,只有在需要复用代码为独立库时才拆分为多模块!

posted @   Sakana~  阅读(112)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示