2026 年 10 月 30 日 发布

装 Go 不是装编译器:模块路径说不清,后面 import 全飘

装 Go,很多人停在 go version 有输出。那只是买了锤子。

真正决定你后面能不能 go get、能不能被别人引用、能不能在 CI 里复现的,是第一次 go mod init 写下的那一行模块路径。 编译器管「能不能编译」;模块路径管「这段代码在世界上叫什么、别人怎么指到它」。后者说不清,前者装得再新也救不了 import。

我自己踩过的坑很具体:本机一切正常,一进 CI 或换同事机器,import 就飘——根因几乎总是第一天随便起了个 demo / test,后来再也没改。

所以这篇想分享的判断很简单:装 Go 不是装一个编译器,是先把模块路径说清楚——否则后面所有 import 都会飘。 坐标钉死了,后面再谈语法、错误、接口才站得住;坐标漂着,排错会先浪费在「包名对不对」上。

先别活在 GOPATH 里

GOROOT 是 toolchain 自己住的地方,一般不用手改。GOPATH 还在,但默认工作模式早已是 modules。新项目不该再把代码硬塞进 GOPATH/src,也不该靠 GO111MODULE=off 续命。你若还在用「必须放到 GOPATH 下面才找得到包」的排错习惯,先卸掉——报错信息会带你去错误的方向。

装好后自查两下就够:

go version
go env GOROOT GOPATH GO111MODULE

有版本号、GO111MODULE 为 on 或空(空也常等价于开启),就可以往下走。自查的意义不是仪式感,而是确认:你接下来所有命令,都跑在 modules 坐标系里。

模块路径不是绰号,是 import 前缀

在干净目录里初始化:

mkdir -p hello-a01 && cd hello-a01
go mod init github.com/you/proj

go.mod 里的 module 行,就是这个模块在世界上的身份证。同仓库里别的包要被引用时,import 路径是:

<module path>/<相对目录>

例如模块是 github.com/you/proj,子目录 internal/greet,别人会写 github.com/you/proj/internal/greet。路径和目录对不上,编译器不会「猜你想引用谁」。

文中 github.com/you/proj 是占位坐标,换成你控制的路径即可。作者本机练习在私有仓的 examples/A01,那只是自用坐标,不是给读者 go get 的公开模块。

模块路径等于 import 前缀

本机写 go mod init demo 往往也能 go run。痛在后面:要发布、要被别的模块引用、要和代理缓存对齐时,随便起的路径会变成长期债务。第一天就起一个你愿意长期用的路径——常见是 github.com/<user>/<repo> 或其子路径,而不是 test、app、tmp。

不确定当前命中哪份 go.mod 时,跑一句 go env GOMOD,看绝对路径,避免在错误目录里 go run 却以为模块配好了。

跑通 run,再用 test 罩住一个纯函数

入口包是 package main,func main 是进程起点。建议同时写一个不碰 I/O 的纯函数(例如问候语拼接),让逻辑和副作用分开。然后把三种命令都跑绿。

greet.go:

package main

func Greeting(name string) string {
	return "你好," + name
}

main.go:

package main

import "fmt"

func main() {
	fmt.Println(Greeting("Go"))
}
go run .
# 期望输出:你好,Go

go build -o hello .
./hello

再给纯函数加测试 greet_test.go:

package main

import "testing"

func TestGreeting(t *testing.T) {
	got := Greeting("Go")
	want := "你好,Go"
	if got != want {
		t.Fatalf("Greeting() = %q, want %q", got, want)
	}
}
go test .
go test -v .

go run 证明能执行,go build 证明能产出二进制,go test 证明核心逻辑可回归。三个都绿,这一段才算钉住——不是只打印了「你好,世界」。单测此刻不必上表驱动;能 TestXxx + 失败时 t.Fatalf 就够。

本机 go version / go test / go run 验证

若你从别的语言转来,容易把「项目名」和「模块路径」当成一回事。项目名可以叫 hello,模块路径却应是可寻址的坐标。团队协作时尤其明显:同事 go get 到的字符串,必须和你 go.mod 里写的一致,少一段路径、多一个大小写,都会变成「找得到仓库却 import 失败」。

两个最常见的坑

一是还在用旧式 GOPATH 心智排错。前者让报错更绕:代码不在 GOPATH/src 就「找不到包」、拒绝认 go.mod。当代默认是 modules;用 go mod init,不要把项目硬塞回 src/。

二是模块路径当临时绰号。后者让日后 go get 全面飘。再补一句实操:改完 go.mod 的依赖后记得 go mod tidy,不要长期手改却不 tidy。

常见坑就这两条:旧式 GOPATH 心智,和路径当绰号。前者让报错更绕,后者让日后全面飘。

装完编译器只是买了锤子;把模块路径钉死,才是把钉子的位置画清楚。 后面变量、错误、接口,都建立在这个不飘的坐标上。下次新建一个小工具时,先问自己一句:这一行 module path,我愿不愿意让别人按它来 import?

先这样,回头见。点个「赞」让我知道你来过,还没关注的话顺手点一个。

← 返回首页