1. 先搞清楚这一节要带你走到哪第一次接触 Go 的人通常会遇到三种情况要么是在项目里看到同事用 Go 写后端服务性能好部署又方便要么是刷招聘网站时发现 Go 工程师的岗位和薪资都在涨要么是研究云原生、区块链、微服务这些热门方向时发现生态里到处都有 Go 的身影。不管你是哪一种第一章第五节“第一个 go 程序”要做的就一件事把你从“听说过 Go”推到“亲手把 Go 跑起来”。这一节的目标非常明确在你自己的电脑上装好可用的 Go 开发环境手写一份最简单的 Go 源码然后用 Go 自带的工具链把它编译并运行。整个过程不涉及复杂语法、不涉及框架选型、不涉及并发模型纯粹是打地基。它适合真正的零基础新手也适合已经会 Java、Python、C 但想快速切入 Go 语法的开发者。我见过太多人栽在第一步。代码写得一点问题没有愣是卡在环境上PATH 没配对、GOPATH 概念搞混、安装包版本选错一晚上就这么没了。所以这一节不打算只给你一串命令复制粘贴而是把环境、编码、运行、排错四个环节完整拆开每一段都会解释背后的原因。你看完不仅能跑通还能理解 Go 工具链到底在做什么。2. 动手前先把 Go 环境配好2.1 Windows 上最省事的 zip 包安装法Windows 用户安装 Go 有两个主流途径一个是官方提供的 MSI 安装包双击一路 Next 就行另一个是官方提供的 zip 压缩包解压后手动配环境变量。两种方式各有优势MSI 会自动帮你写注册表、配 PATH对新手最友好zip 包则更干净解压到哪就是哪卸载时直接删文件夹而且很多 CI 脚本和自动化部署流程里也习惯用 zip 包方式。这一节就以 zip 包方式为例因为它能让你更清楚地看到 Go 环境到底由哪几部分组成。具体操作分三步。第一步去 Go 官方下载页面选择 Windows 对应的 zip 包。这里有个常见坑绝大多数现代电脑是 64 位系统要下载 amd64 版本只有极老的机器才可能用 386 版本arm64 版本是给 Windows on ARM 设备用的普通用户不需要。第二步把 zip 包解压到你指定的安装位置比如 C:\go。解压后你会看到 bin、src、pkg 等目录bin 目录里就有 go.exe。第三步把 C:\go\bin 加到系统 PATH 环境变量里这一步决定了你在命令行敲 go 的时候系统能不能找到它。添加 PATH 的具体路径是右键“此电脑”进入属性选高级系统设置点环境变量在系统变量里找到 Path点编辑新建一条填 C:\go\bin。这里要留意如果之前装过其他版本的 GoPATH 里可能会残留 C:\Program Files\Go\bin 之类路径最好清理掉否则命令行里敲的到底是哪个 go你自己都说不清楚。配好之后一定要打开一个新的命令行窗口——旧窗口不会刷新环境变量——然后敲 go version看到版本信息就说明安装成功了。2.2 Ubuntu Server 上安装 Go 的两种思路Linux 用户尤其是 Ubuntu Server 上做服务端开发的同学安装 Go 的方式和 Windows 不完全一样。最常见的手段是用 apt 安装sudo apt update sudo apt install golang-go一条命令搞定。但我个人更推荐官方 tar.gz 包的安装方式原因是 apt 仓库里的 Go 版本往往滞后官方已经发布新版本了apt 源里可能还停留在旧版本。版本滞后不是小问题Go 的很多新特性比如性能优化、工具链增强都依赖较新版本。写第一个程序无所谓后面写真实项目就会遇到“语法明明没错但编译器不认识”的尴尬。tar.gz 包方式的操作分三步。第一步下载对应架构的安装包x86_64 的服务器选 amd64 版本。第二步解压到 /usr/localsudo tar -C /usr/local -xzf go1.22.x.linux-amd64.tar.gz这样 Go 就安装到了 /usr/local/go 目录下。第三步把 /usr/local/go/bin 加进 PATH常见做法是编辑 ~/.profile 或 /etc/profile.d/go.sh追加一行 export PATH$PATH:/usr/local/go/bin。设置完成后执行 source ~/.profile 或者重新登录终端再敲 go version 验证。这里有个容易被忽视的细节如果你之前用 apt 装过旧版 Go卸载时不能只删 /usr/local/go因为 apt 会把可执行文件放在 /usr/bin/go 里。先执行 sudo apt remove golang-go 清理干净再装官方包否则 which go 会同时搜到 /usr/bin/go 和 /usr/local/go/bin/go 两个结果导致版本混乱。我排过不少环境问题最后发现都是这类“残留版本”引起的。2.3 环境算不算配好用三个命令验证环境配得对不对不只看 go version 有没有输出。我一般会做三个检查分别对应三个不同环节。第一go version 能输出版本号说明主程序可用PATH 基本没问题。第二随便 cd 到任意目录敲 go env GOPATH这个命令会打印当前 GOPATH 的值Windows 上默认是 C:\Users\你的用户名\goLinux 上是 /home/你的用户名/go。第三临时建一个目录写一个最简单的 go 文件用 go run 跑通这一步才是真正验证“工具链能不能完成一次完整编译”。为什么要单独查 GOPATH因为很多新手在早期资料里看到过 GOPATH 的用法会手动去设一个 GOPATH 指向自己的代码目录结果后面用 go mod 时又和 Modules 冲突出现一堆莫名其妙的报错。实际上从 Go 1.11 开始 Modules 就是官方推荐的依赖管理方式从 Go 1.16 开始 Modules 默认启用现在开发时你几乎不需要手动设置 GOPATH。它默认指向用户目录下的 go 文件夹里面存的是依赖包缓存不是项目源码。这个认知如果一开始就建立好后面会少走很多弯路。3. 从零写出第一份 Go 代码3.1 项目目录应该怎么建很多教程一上来就让你写 hello.go却不说文件放哪。这看起来是小事实际上关系到你对 Go 项目结构的第一印象。Go Modules 时代项目结构已经非常自由你可以随意新建一个文件夹在里面初始化一个 module然后正常写代码不需要像早期 GOPATH 时代那样必须把项目放在 $GOPATH/src 下面。我建议你新建一个专门的学习目录比如 D:\golearnWindows或者 ~/golearnLinux在里面再建一个 hello 子目录专门放这个第一个程序。为什么要单独建子目录因为一个 go module 的根目录通常对应一个独立项目这个 hello 项目虽然只包含一个文件但保持“一个项目一个目录”的习惯后面你写第二个、第三个程序时就不会弄混。在 hello 目录里打开终端执行 go mod init hello你会看到目录里多了一个 go.mod 文件。这个文件内容很简单第一行是 module hello表示这个模块的名字叫 hello下面一般跟着一行 go 版本号。go.mod 是 Go Modules 的核心记录了这个项目的依赖关系虽然目前没有任何外部依赖但提前建立这个文件能让 go run、go build 等命令正常工作。有个细节值得提一下go mod init 后面的名字不一定要和目录名一致它只是模块标识你可以叫它 hello、test、myfirstapp甚至可以写成类似 github.com/yourname/hello 的完整路径。我写学习项目时喜欢用简短名字写真实项目才用带路径的全名。这个习惯不影响编译但能让 go.mod 看起来更规范。3.2 逐行编写 hello.go在 hello 目录下新建一个文件命名为 hello.go注意后缀必须是 .go。用任何文本编辑器都可以新手我推荐直接用 VS Code装上官方 Go 扩展后写代码会有语法高亮、自动补全和保存时自动格式化这对建立良好的代码风格帮助很大。下面是我写过无数次的第一份 Go 程序package main import fmt func main() { fmt.Println(Hello, Go!) }就这么五行我们逐行来看。第一行 package main 声明这个文件属于 main 包。Go 程序由一个或多个包组成包是代码组织的基本单位。这里的关键点是只有 package main 的包才能被编译成可执行文件。如果你写的是 package foo后面执行 go build编译器不会生成可以直接运行的程序只会生成一个可以被其他包引用的库。这个区别一定要在第一天就记住因为它解释了为什么所有 Go 可执行程序的入口文件都长这样。第二行 import fmt 是导入标准库里的 fmt 包。fmt 是 format 的缩写这个包提供了格式化输入输出的能力最常用的就是 Println 系列函数。你可以把 import 理解成“引入工具箱”fmt 就是语言自带的一个常用工具箱。Go 对导入语句有一个强制要求导入的包必须被使用否则编译直接报错。这和其他很多语言不一样它的目的是杜绝无用的依赖防止代码库臃肿。我第一次在 Go 里写了个多余的 import编译器直接拒绝通过当时觉得烦后来习惯了反而认为这个约束很合理。第三行 func main() 声明了一个名为 main 的函数。在 Go 里函数用 func 关键字声明后面跟函数名、参数列表、返回值列表。main 函数是整个程序的入口运行时从这一行开始执行。这里有个硬性规则每个可执行的 main 包必须包含一个 main 函数而且一个程序只能有一个入口。当然如果你写的是被其他项目引用的库包就不需要 main 函数因为库不是直接运行的程序。第四行和第五行是一对大括号里面写着函数体也就是 main 函数实际要执行的代码。fmt.Println(Hello, Go!) 做的事情很简单向标准输出打印一行文本打印完自动换行。这里的字符串用双引号包起来Go 里单引号和双引号有严格区分单引号表示单个字符双引号表示字符串。这个细节和不少语言不一样后续写复杂代码时容易踩坑不过第一个程序还涉及不到。3.3 三个必须理解的核心概念写完了 hello.go你的任务还没结束。如果只是复制粘贴然后运行成功过三天你大概率什么都记不住。我建议你花五分钟把下面三个概念在脑子里过一遍它们是你之后看懂一切 Go 代码的基础。第一个是“包package”。前面说了Go 源码文件的第一行永远是 package 声明。你可以把包理解成一个命名空间或者文件夹它把相关代码组织在一起。标准库里包名通常和目录名一致比如 fmt 包的源码就在 $GOROOT/src/fmt 目录下。你自己写项目时一个包对应一个目录是约定俗成的规则这个规则保证了代码可读性看到 import myapp/utils你就知道这个文件要用到 myapp 项目里 utils 目录下的代码。第二个是“导入import”。import 后面跟的是包的路径不是包名。这句话很关键。比如你导入 fmt编译器会去标准库目录下找 fmt 这个包如果你导入的是自己写的子包路径就是从 module 根目录开始的相对路径。理解了这一点后面遇到 import 报错时先检查路径对不对而不是怀疑包本身的代码有错。第三个是“入口函数main”。C 语言有 mainJava 有 main 方法Go 也有 main 函数它是程序启动时第一个执行的代码位置。Go 对 main 函数有一个额外要求它不能有参数也不能有返回值。这和 C、Python 都不一样后者通常能通过命令行参数把外部数据传进来。Go 里获取命令行参数要用 os.Args这是后话但你现在知道标准 main 函数长什么样就足够了。4. 运行、编译与验证4.1 go run 和 go build 到底差在哪写完代码接下来就是运行。Go 提供了两个高频命令go run 和 go build。很多新手搞不清它们的区别其实一句话就能概括go run 是编译并立刻运行跑完不留痕迹go build 是编译成可执行文件你自己去运行。具体来说在 hello 目录里执行 go run hello.goGo 工具链会把源码编译成临时二进制文件放在临时目录里然后启动它。你会在终端看到 Hello, Go! 的输出但当前目录里什么额外文件都没有生成。这个过程对学习尤其合适因为你每改一次代码重新 go run 一次就能立刻看到结果省去管理中间产物的麻烦。缺点也很明显每次都要重新编译大项目会拖慢启动速度而且临时二进制不会留在你手上没法分发给别人用。go build 则完全相反。执行 go build 后当前目录会出现一个可执行文件文件名默认是模块名。拿 hello 项目来说Windows 下你会看到 hello.exeLinux 下是 hello。这个文件就是编译产物可以双击运行可以从命令行执行也可以复制到别的机器上直接跑前提是目标系统和当前系统兼容。go build 默认不带任何优化参数产物体积可能偏大但没关系第一个程序我们关注的是流程跑通。我用一张表把两者的区别列出来方便你之后快速查阅对比项go rungo build是否生成文件不生成使用临时目录生成可执行文件使用场景开发调试、快速验证发布部署、分发产物命令格式go run hello.gogo build 或 go build hello.go运行方式自动编译并执行手动执行生成的二进制适合人群学习阶段、修改频率高项目成型、需要交付4.2 完整走一遍编译流程我把整个流程从头到尾走一遍你照着操作就行。假设你已经在 Windows 上配好了环境并且建好了 D:\golearn\hello 目录里面目前为空。第一步打开命令行cd 到 D:\golearn\hello。第二步执行 go mod init hello如果提示 go.mod already exists说明你之前已经初始化过了没关系。第三步把上文那段 hello.go 代码保存到目录里然后执行 go run hello.go。你会看到终端输出 Hello, Go!。到这一步你的第一个 Go 程序已经成功运行了。接下来试一下构建。执行 go build然后执行 dirWindows或者 ls -lLinux你会发现目录里多了一个 hello.exe 或 hello 文件。再执行 .\hello.exeWindows或 ./helloLinux会看到相同输出。注意一个小差异go run 后面跟的是源文件名 hello.go而 go build 后面什么都不跟它默认编译当前目录下的整个 module。当然go build hello.go 这种写法也合法它会生成以源文件名为基础的可执行文件但更常见的做法是直接敲 go build。这一步还可能遇到一个新手常见困惑go build 执行后没有任何输出提示直接静默结束了。这不是出错了Go 的设计风格就是“没有消息就是好消息”只有编译失败时才会打印错误信息。程序员的第一个直觉往往是“是不是少了点什么”其实什么都没少去看看目录里多出来的那个文件就是最好的成功证明。4.3 从 hello world 延伸出去跑通第一个程序之后我建议你做两件事让这次学习的效果最大化。第一件事改动输出文本。把 Hello, Go! 改成你自己的名字比如 fmt.Println(Hello, 张三!)重新 go run。这看起来无聊实际上是在训练你建立“改代码—重新编译—看结果”的反馈循环。这个循环是你后面写任何程序的核心工作方式越早习惯越好。第二件事多打印一行。在 main 函数里再加一行 fmt.Println(I am learning Go.)注意新的一行要写在 main 函数的大括号里面缩进用 tab 键或者 VS Code 默认配置都行。Go 官方推荐 tab 缩进gofmt 工具会自动帮你规范化格式。保存后 go run你会看到两行输出。这时候你就能直观感受到“函数体里的代码按顺序执行”这个最基本的执行模型。我见过不少学员学完这一节后急着去看并发、看 goroutine、看接口结果基础概念没吃透后面越学越吃力。第一个程序的意义不在于它能干什么而在于它把你和工具链之间的桥梁搭起来了。后面无论你写多复杂的项目归根结底都是 package、import、func 这三个基本要素的组合。5. 新手必踩的坑排错实录5.1 命令行里找不到 go这是出现频率最高的问题症状是敲 go version 或 go run 时终端提示“go 不是内部或外部命令”Windows或者 command not foundLinux。原因只有一个系统在 PATH 环境变量里找不到 go 这个可执行文件。Windows 下的排查步骤先确认安装目录里的 bin 下确实有 go.exe再打开环境变量编辑器确认 Path 里有 C:\go\bin 这一类条目最后确认敲命令的那个命令行窗口是新开的。我想强调一下“新开窗口”这件事Windows 的环境变量是在进程启动时读取的已经开着的窗口不会自动刷新所以改完 PATH 后必须把命令行窗口全部关掉再重开。这个操作我至少帮别人排查过十几次每次都发现是没重开窗口。Linux 下的排查步骤先 which go 看系统能不能找到你期望的那个 go如果输出 /usr/local/go/bin/go说明 PATH 没问题如果没有输出检查 /etc/profile 或 ~/.profile 里的 export 语句是否写对了。尤其是 export PATH$PATH:/usr/local/go/bin 这句$PATH 不能丢丢了会把原有路径全部冲掉然后你会发现 ls、vi 这些基本命令都找不到了。这个坑我踩过一次教训深刻所以每次配置 PATH 都先看一眼原来那行是什么样子。5.2 go run 报语法错误或 undefined如果你抄了代码却出现 expected ;, found EOF 或者 undefined: fmt 之类的提示九成是代码本身的问题。最常见的三种情况第一文件编码不是 UTF-8Windows 记事本默认编码存出来的文件可能是 ANSIGo 编译器只认 UTF-8解决办法是用 VS Code 或 Notepad 把文件转成 UTF-8 编码再保存第二大括号没配对比如 main 函数少写了一个 }编译器会在文件末尾报 EOF 相关错误第三import 写的是 FMT 或者 fmt 后面多了空格Go 对大小写和空格敏感必须严格小写。我建议新手遇到编译错误时先看错误信息里给出的文件位置和行号。Go 编译器报错还算友好通常能直接指出第一处出错的位置。先把第一处错误改掉重新编译很多时候后面的错误会跟着消失因为编译器在第一个错误之后可能会出现连锁误报。不要想着一次性把所有错误都看完逐次修复才是效率最高的方式。5.3 运行了但没看到任何输出有一种情况比较隐蔽go run 执行了没有报错但终端里空荡荡的什么都没看到。这个问题通常出在你改了输出内容上。比如你把代码改成了 fmt.Print(Hello)而不是 Println——Print 和 Println 的区别是Print 打印完不会自动换行。如果在一个交互式终端里运行看起来就像什么都没输出其实内容是有输出的只是没有换行让输出“浮”出来了。还有一种可能是你的程序里用了 fmt.Printf 但格式字符串写错了比如想打印字符串却用了数字占位符导致输出不符合预期。最直接的排查方法是在代码里加一行 fmt.Println(checkpoint) 作为标记看看这行能不能输出。只要这行能输出说明程序执行本身没问题问题出在目标输出语句上。这种插桩排查法虽然原始但在没有调试器的场景下非常管用我在实际项目里也经常用。5.4 版本混乱莫名跑的是旧版前面提到过 apt 安装和官方 tar.gz 包共存的问题。如果你在 Linux 上执行 go version 看到版本号偏旧又确认自己装的是新版执行 which go 看看路径。如果输出的是 /usr/bin/go说明系统优先找到的是旧版。处理办法是把 /usr/local/go/bin 放到 PATH 前面或者彻底卸载 apt 版本。Windows 上也会出现类似问题早期装过 MSI 版 Go后来手动解压了 zip 包两个版本并存PATH 里两个路径都在系统按顺序找到先匹配的那个。建议只保留一个版本的 Go把另一个的 PATH 条目删掉。版本统一这件事越早做越好因为当你开始用 go mod 管理依赖时依赖包版本和 Go 工具链版本是有兼容关系的新版本能跑的代码老版本可能就跑不起来。6. 我的几点真实体会写到这里“第一个 go 程序”这个主题已经讲透了。最后分享几点我在带新人过程中积累的体会算是不在教科书上的内容。第一学 Go 的第一个程序千万别急着抄复杂代码。我见过有同学第一天就想写一个 HTTP 服务器代码抄过来了也能跑但出了错完全不知道去哪查因为连最基本的运行原理都没建立。从 hello world 开始按部就班地把每个概念吃透反而是最快的一条路。第二尽早养成查看官方文档的习惯。Go 的官方文档做得很好标准库每个函数都有详细说明和示例。比如 fmt.Println 这个函数你想知道它多参数怎么处理、返回什么值直接查官方文档比翻博客、问工具都要靠谱。因为在学习阶段信息源的正误直接影响你建立的知识框架对不对。第三也是我最想强调的一点第一个程序跑通之后不妨顺手试一下 Go 的自动格式化工具 gofmt。VS Code 的 Go 扩展默认在保存时自动执行 gofmt它会帮你把缩进、括号、换行都调整成标准风格。养成“写完代码就格式化”的习惯对 Go 这种对代码风格有统一要求的语言来说越早建立后面看别人的代码、被别人看你的代码时都会舒服很多。这个 hello world 项目现在看起来简单但它后面可以延伸的方向非常多你可以在它基础上加一个命令行参数让程序打印出你输入的名字可以加一个循环把一句话打印十遍甚至可以加一个 HTTP 接口让浏览器访问后返回一段文字。每个方向都会用到现在打下的这些基础。Go 这条路我走了很久回头看这一步走得稳不稳直接决定了后面走得顺不顺。