自定义包必须放在模块根目录或GOPATH/src对应路径中,import路径须与go.mod声明的模块名及子目录名严格匹配,禁止使用./相对路径;包名须全小写且与目录名一致,导出标识符首字母必须大写。

自定义包必须放在模块根目录下或 GOPATH/src 对应路径中
Go 不允许直接 import 一个本地目录(比如 ./utils)来跨项目使用,除非你明确启用 Go Modules 并在模块内组织结构。如果你看到 cannot find package "utils" 或 import "./utils": cannot import relative path,说明当前工作目录没有 go.mod,或者导入路径没对上模块声明。
正确做法是:在项目根目录执行 go mod init myproject(模块名建议用域名格式,如 github.com/you/myproject),然后把自定义包放在子目录里,例如:
myproject/
├── go.mod
├── main.go
└── utils/
└── string.go
此时 utils/string.go 的包声明必须是 package utils,且导入时写成 "myproject/utils"(不是 "./utils",也不是 "utils")。
- 模块名(
go.mod第一行)决定导入路径前缀 - 子目录名决定导入路径后缀,二者拼起来才是完整 import 路径
- 不推荐用
./xxx相对导入——它只在go run单文件模式下临时有效,无法被其他项目复用
函数/变量首字母大写才可导出
Go 的可见性规则非常严格:只有首字母大写的标识符(PrintStr、Version、Config)才能被外部包访问;printStr、version、config 在包外完全不可见,编译会报 cannot refer to unexported name xxx。
这个规则和包名、文件名、目录名都无关,只看符号本身是否“导出”。常见错误包括:
- 写了
func helper()然后在 main 中调用utils.helper()→ 报错 - 包里定义了
var count = 0,想在外部读取 → 不行,得改成var Count = 0 - 类型嵌套字段小写,如
type User struct { name string }→ 外部无法访问name
记住:导出与否,只看第一个 Unicode 字母是否为大写(不是 ASCII,所以中文、希腊字母等也适用规则)。
包名必须与所在目录名完全一致且全小写
如果目录叫 validators,那么所有 .go 文件顶部必须写 package validators;写成 package Validators 或 package validators_v2 都会导致 go build 失败,报 invalid package name 或 found packages xxx and yyy in same directory。
这个约束是硬性的,不是风格建议。尤其注意:
- 不能含下划线(
my_utils❌)、不能含大写字母(MyUtils❌)、不能是 Go 关键字(type❌) - 多个
.go文件必须声明相同包名,哪怕只有一个文件也要遵守 - 包名可以和导入路径最后一段不同(比如目录
github.com/you/pkg/v2下包名仍是v2),但强烈建议保持一致,否则容易混淆
go run 只能运行 package main,别试图直接运行自定义包
当你执行 go run utils/string.go,一定会遇到 go run: cannot run non-main package。这不是 bug,是 Go 构建模型的强制设计:自定义包只是库,不是可执行单元。
正确流程永远是两步:
- 写一个独立的
main.go(必须package main+func main()) - 在它里面
import "myproject/utils",再调用utils.Reverse("abc")
不要把 main.go 塞进 utils/ 目录里,也不要给自定义包加 func main() —— 这会让 Go 认为它是可执行程序,反而破坏复用性。
最易被忽略的一点:自定义包里的 init() 函数会在被导入时自动执行,且顺序不可控。如果包里有副作用(比如改全局变量、开连接、读配置),要特别小心初始化时机。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











