编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

  • Cpp入门到精通

  • Java入门精通

  • Go入门到精通

    • 入门教程

      • README
      • Go简史
      • 基础语法
        • 目录介绍
        • 2.1 本章学习目标
        • 2.2 安装 Go 1.22 与环境验证
          • 2.2.1 三大平台安装速览
          • 2.2.2 GOROOT / GOPATH / GOPROXY 三件套
        • 2.3 第一个 Go 模块:现代 Hello World
          • 2.3.1 go mod init 起手
          • 2.3.2 写 main.go 并运行
          • 2.3.3 go run vs go build
        • 2.4 包与导入
          • 2.4.1 package main vs 库包
          • 2.4.2 import 的四种写法
          • 2.4.3 大写导出 / 小写未导出
        • 2.5 标识符、关键字、操作符
          • 2.5.1 25 个关键字一览
          • 2.5.2 命名规范(驼峰、缩写大写)
          • 2.5.3 预声明标识符(int / true / nil ...)
        • 2.6 变量与常量
          • 2.6.1 var / := / const 三件套
          • 2.6.2 多变量声明与并行赋值
          • 2.6.3 iota 枚举模式
        • 2.7 注释与文档(godoc)
        • 2.8 综合示例:能跑的猜数字游戏
        • 2.9 本章底层原理(简介)
        • 2.10 Go 新手陷阱 Top 5
          • ❌ 陷阱 1:import 写了但没用
          • ❌ 陷阱 2:var x int = 0 冗余写法
          • ❌ 陷阱 3::= 在已有变量上误用
          • ❌ 陷阱 4:go run main.go 而不是 go run .
          • ❌ 陷阱 5:把 go mod tidy 当成"普通命令"乱跑
        • 2.11 思考题
        • 2.12 推荐阅读
          • 卷内
          • 跨卷
          • 外部资料
      • 数据类型
      • 运算符
      • 复合类型
      • 流程语句
      • 函数
      • 指针与逃逸
      • 结构体与方法
      • 接口与多态
      • 错误处理
      • 并发goroutine
      • 通道channel
      • 同步sync包
      • IO和文件
      • 标准库与泛型
      • 工程化与模块
      • 特性图谱
    • 综合案例

    • 专栏博客

    • 开发技巧

  • JavaScript入门

  • CodeX
  • Go入门到精通
  • 入门教程
杨充
2026-05-21
目录

基础语法

# 第 2 章 基础语法与 Hello World

现代 Go(1.22)的第一行代码——以 go mod 起手,不学 GOPATH 老模式。 关键词:package、import、go run、go build、go mod init、命名规范、25 个关键字


# 目录介绍

  • 2.1 本章学习目标
  • 2.2 安装 Go 1.22 与环境验证
    • 2.2.1 三大平台安装速览
    • 2.2.2 GOROOT / GOPATH / GOPROXY 三件套
  • 2.3 第一个 Go 模块:现代 Hello World
    • 2.3.1 go mod init 起手
    • 2.3.2 写 main.go 并运行
    • 2.3.3 go run vs go build
  • 2.4 包与导入
    • 2.4.1 package main vs 库包
    • 2.4.2 import 的四种写法
    • 2.4.3 大写导出 / 小写未导出
  • 2.5 标识符、关键字、操作符
    • 2.5.1 25 个关键字一览
    • 2.5.2 命名规范(驼峰、缩写大写)
    • 2.5.3 预声明标识符(int/true/nil...)
  • 2.6 变量与常量
    • 2.6.1 var / := / const 三件套
    • 2.6.2 多变量声明与并行赋值
    • 2.6.3 iota 枚举模式
  • 2.7 注释与文档(godoc)
  • 2.8 综合示例:能跑的猜数字游戏
  • 2.9 本章底层原理(简介)
  • 2.10 Go 新手陷阱 Top 5
  • 2.11 思考题
  • 2.12 推荐阅读

# 2.1 本章学习目标

  • ✅ 在自己电脑装好 Go 1.22 并跑通 go run 与 go build
  • ✅ 理解 go mod init / go mod tidy 的基本用法
  • ✅ 能背出 Go 的 25 个关键字
  • ✅ 能解释为什么"导出"用首字母大小写、为什么没有 public / private
  • ✅ 能默写 iota 枚举模式
  • ✅ 知道 var / := / const 在何处该用何处不该用

# 2.2 安装 Go 1.22 与环境验证

# 2.2.1 三大平台安装速览

平台 推荐方式 验证
macOS brew install go(或官网 pkg) go version
Linux 官网 tar.gz 解到 /usr/local/go,PATH 加 /usr/local/go/bin 同上
Windows 官网 msi 同上

无论哪种方式,最终都应该看到:

$ go version
go version go1.22.0 darwin/arm64
1
2

⚠️ 基线提醒:本书所有代码以 Go 1.22 为基线。如果你装的是 1.21 或更早,部分新特性(如循环变量每轮新建、for i := range N)会跑不通。

# 2.2.2 GOROOT / GOPATH / GOPROXY 三件套

环境变量 含义 是否需要手动设置
GOROOT Go 的安装目录(含 SDK 源码、标准库) ❌ 不要设。包管理器会自动处理
GOPATH 旧时代"工作区",modules 时代仅存放 go install 安装的二进制(默认 ~/go) ❌ 一般不要动
GOPROXY 模块下载代理 ✅ 国内强烈建议设置
GOSUMDB 模块哈希验证服务 视环境而定

国内开发者推荐配置(一次设置,永久生效):

go env -w GOPROXY=https://goproxy.cn,direct
go env -w GOSUMDB=sum.golang.google.cn
1
2

go env -w 会把这些设置写入 ~/.config/go/env(或 Windows 下的对应位置),不污染 shell 环境。

💡 GOPATH 已经"死了"吗? 不完全。$GOPATH/bin 仍是 go install 默认产物目录,建议把它加入 PATH:export PATH=$PATH:$(go env GOPATH)/bin。这样 go install xxx@latest 装的工具能直接调用。


# 2.3 第一个 Go 模块:现代 Hello World

我们不走 "在 GOPATH 下建目录" 这条已死的路,直接 go mod init。

# 2.3.1 go mod init 起手

任意目录(不必在 GOPATH 下):

mkdir hello && cd hello
go mod init example.com/hello
1
2

执行后会生成一个 go.mod 文件:

module example.com/hello

go 1.22
1
2
3

三件事说明:

  1. module example.com/hello:本模块的"名字",也就是别人 import 你时写的路径。如果你打算开源到 GitHub,用 github.com/yourname/hello 起名最合适。
  2. go 1.22:本模块要求的最低 Go 版本(也作为语言行为的开关,比如循环变量语义)。
  3. 还没有 require:因为我们还没引用任何第三方库。

# 2.3.2 写 main.go 并运行

在 hello 目录新建 main.go:

package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界")
}
1
2
3
4
5
6
7

逐行解释:

  • package main — 本文件属于哪个包。包名为 main 是特殊的:表示这是一个可执行程序,不是库。
  • import "fmt" — 引入标准库 fmt(format)。如果引入了不用,编译器直接报错——这是 Go 的"零容忍"风格。
  • func main() — 程序入口;签名固定为 func main(),无参数无返回值。
  • fmt.Println(...) — 调用 fmt 包中的导出函数 Println(首字母大写 = 导出)。

运行:

$ go run .
Hello, 世界
1
2

注意命令是 go run .(当前目录)而不是 go run main.go。前者会把所有 .go 文件一起编译,后者在多文件项目里会找不到符号。养成 go run . 的肌肉记忆很重要。

# 2.3.3 go run vs go build

命令 行为 产出
go run . 编译并立即运行,临时二进制随即删除 无
go build 编译,产物落到当前目录 ./hello(与目录同名)
go build -o myapp 同上但指定产物名 ./myapp
go install 编译并把产物放到 $GOPATH/bin ~/go/bin/hello
$ go build
$ ls
go.mod  hello*  main.go

$ ./hello
Hello, 世界
1
2
3
4
5
6
  • 注意产出 hello 是静态链接的二进制——没有 JVM、没有 .dll、没有 node_modules,scp 到任意一台同架构同操作系统的机器都能跑。这是 Go 在容器时代的"杀手锏"。
  • 如果要跨平台编译,加环境变量即可:
GOOS=linux  GOARCH=amd64 go build -o hello-linux
GOOS=darwin GOARCH=arm64 go build -o hello-mac
GOOS=windows GOARCH=amd64 go build -o hello.exe
1
2
3

# 2.4 包与导入

# 2.4.1 package main vs 库包

                  package main          其他任意 package(如 utils、auth)
                  ────────────          ──────────────────────────────
位置                  顶层               任意子目录
是否能跑              ✅ 直接 go run     ❌ 只能被 import
是否要 main()         ✅ 必须            ❌ 不要写
能否被 import         ❌ 不能            ✅ 可以
1
2
3
4
5
6

一个常见的多包项目布局:

myapp/
├── go.mod
├── main.go          ← package main
├── auth/
│   └── auth.go      ← package auth
└── store/
    └── store.go     ← package store
1
2
3
4
5
6
7

main.go 里 import "example.com/myapp/auth",对应硬盘上的 auth/ 子目录。目录名与包名最好一致——这是约定,不是强制。

# 2.4.2 import 的四种写法

// 1. 单条导入
import "fmt"

// 2. 分组导入(推荐)
import (
    "fmt"
    "strings"

    "github.com/google/uuid" // 第三方与标准库之间留空行
)

// 3. 别名导入:解决重名 / 起短名
import (
    crand "crypto/rand"
    mrand "math/rand"
)

// 4. 副作用导入:只为执行 init(),不直接用包内符号
import _ "github.com/lib/pq"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

何时用别名:两个不同路径但同名包共存(最常见的就是 crypto/rand 与 math/rand)。

何时用 _:需要触发包的 init() 副作用——比如数据库驱动注册(database/sql 通过 init() 注册 driver)。

# 2.4.3 大写导出 / 小写未导出

Go 没有 public / private / protected 关键字,靠首字母大小写:

标识符 可见性
Foo / BarBaz 导出(包外可见)
foo / barBaz 未导出(仅本包内可见)
package mathx

func Square(x int) int {  // ✅ 包外可调用
    return x * x
}

func double(x int) int {  // ❌ 包外不可见
    return x * 2
}
1
2
3
4
5
6
7
8
9

设计哲学:约定即语法。这让你 git grep '^func [A-Z]' 就能列出所有公共 API,比在 IDE 里点开类查 public 修饰符快得多。


# 2.5 标识符、关键字、操作符

# 2.5.1 25 个关键字一览

Go 总共只有 25 个关键字——少到可以在脑子里全记下来。背下这张表,你就读得懂任何 Go 代码:

分类 关键字
声明(5) var、const、type、func、package
控制流(11) if、else、for、switch、case、default、break、continue、fallthrough、goto、return
并发(3) go、chan、select
复合类型相关(3) struct、interface、map
其他(3) import、defer、range

仔细数一下——确实只有 25 个。对比 C++ 的 ~100 个、Java 的 ~50 个,Go 的"少即是多"哲学一目了然。

# 2.5.2 命名规范(驼峰、缩写大写)

Go 官方风格指南 + Uber Style Guide 的共识:

元素 风格 例子
局部变量 短小驼峰 i、buf、userID
函数 / 方法 驼峰,导出大写 Marshal / parseHeader
类型 驼峰,导出大写 Server、httpClient
常量 驼峰(不用 UPPER_SNAKE) MaxRetries、defaultPort
包名 全小写、单词、不加下划线 auth、httputil
文件名 小写下划线 user_repo.go、auth_test.go
接口 单方法接口加 -er 后缀 Reader、Writer、Stringer

"缩写全大写"规则——缩写词整体大写或整体小写,不要驼峰:

// ✅ 正确
var userID int        // ID 全大写
var httpClient *Client // http 全小写
func ParseURL(s string) {} // URL 全大写

// ❌ 错误
var userId int        // 缩写 Id 不规范
var HttpClient *Client // 缩写 Http 不规范
func ParseUrl(s string) {} // 同上
1
2
3
4
5
6
7
8
9

golangci-lint 默认开启的 revive / stylecheck 会自动检测这些。

# 2.5.3 预声明标识符(int / true / nil ...)

Go 还有一批预声明标识符——它们不是关键字,但被 universe scope 占用,重定义不会编译错但是反模式:

// 类型
bool  byte  rune  string  error
int  int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64  complex64 complex128
any  comparable  // Go 1.18+

// 常量
true  false  iota  nil

// 函数
make  new  len  cap  append  copy  delete  close
panic  recover  print  println
min  max  clear  // Go 1.21+ 新增内置

// ❌ 反例:不要重新定义这些名字
var nil = 1   // 编译过,但任何看到这行的 reviewer 都会 reject
func len() {} // 同上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2.6 变量与常量

# 2.6.1 var / := / const 三件套

Go 提供三种声明方式,用对场景:

// 1. var:显式声明,可只声明不初始化(自动取零值)
var x int           // x = 0
var name string     // name = ""
var p *Person       // p = nil
var s = "hello"     // 类型由右侧推导

// 2. := 短变量声明:仅在函数内可用,必须初始化
func main() {
    x := 42         // 等价于 var x int = 42
    name := "Bob"   // 等价于 var name string = "Bob"
}

// 3. const:编译期常量,必须能在编译期求值
const Pi = 3.14159
const MaxRetries int = 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

何时用哪个?三条规则:

  1. 包级别(函数外)只能用 var 或 const,不能用 :=。
  2. 函数内首选 :=——更短、有右值时类型自动推导。
  3. 零值就够用时(如 var sum int 当累加器)才用 var,不用写 sum := 0。
// ✅ 推荐
var sum int           // 利用零值,比 sum := 0 简洁
for _, v := range nums {
    sum += v
}

// ❌ 反模式
var x int = 0         // 冗余,写 var x int 即可
sum := 0              // 函数内能用 := 但显式写 0 没必要,var 更简洁
1
2
3
4
5
6
7
8
9

# 2.6.2 多变量声明与并行赋值

// 多个变量一起声明(不同类型)
var (
    name    string
    age     int
    married bool
)

// 同类型批量
var x, y, z int

// 多重赋值(最常用:交换、多返回值接收)
a, b := 1, 2
a, b = b, a              // 一行交换,没有 tmp 变量
v, ok := m["key"]        // map 双返回值
data, err := os.ReadFile("a.txt")  // error 模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

多重赋值是右侧整体先求值后再赋给左侧,所以 a, b = b, a 不需要中间变量。这一点是 Go 的语义保证,与 Python 一致。

# 2.6.3 iota 枚举模式

Go 没有 enum 关键字,用 const + iota 模拟。iota 是const 块内自增的整型计数器,每个 const 块从 0 开始:

const (
    Red   = iota  // 0
    Green         // 1(继承上一行表达式 = iota)
    Blue          // 2
)

// 经典用法:位掩码
const (
    FlagRead    = 1 << iota  // 1 << 0 = 1
    FlagWrite                // 1 << 1 = 2
    FlagExecute              // 1 << 2 = 4
)

// 跳过某个值
const (
    _ = iota  // 0 不要
    KB = 1 << (10 * iota)  // 1 << 10
    MB                     // 1 << 20
    GB                     // 1 << 30
    TB                     // 1 << 40
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

💡 给枚举类型起个独立类型名(type-safe enum):

type Color int

const (
    Red Color = iota
    Green
    Blue
)

func (c Color) String() string {
    return [...]string{"Red", "Green", "Blue"}[c]
}

func main() {
    var c Color = Green
    fmt.Println(c) // Green(自动调用 String 方法)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这种"自定义类型 + 字符串方法"的组合接近 Java/Rust 的 enum 体验。


# 2.7 注释与文档(godoc)

Go 把"注释"和"文档"统一起来——所有以包/函数/类型/常量为目标的注释自动是它的文档:

// Package mathx provides extra math utilities not in the standard library.
package mathx

// Square returns x * x.
//
// It panics if x*x overflows int64.
func Square(x int64) int64 {
    return x * x
}
1
2
3
4
5
6
7
8
9

约定:

  • 包注释紧贴 package 行,第一句应当以包名开头:Package mathx provides ...
  • 导出符号必须有注释,第一句以符号名开头:Square returns ...
  • 注释支持 Markdown-lite(Go 1.19+ 引入了 [link] 风格)

查看:

go doc .              # 当前包文档
go doc fmt.Println    # 标准库符号
go doc -all strings   # 整个包详细
1
2
3

或本地起 web:

go install golang.org/x/tools/cmd/godoc@latest
godoc -http=:6060
# 浏览器访问 http://localhost:6060
1
2
3

线上版:https://pkg.go.dev/ (opens new window) 是 Go 官方文档站。


# 2.8 综合示例:能跑的猜数字游戏

把本章知识全部用上,写一个 100 行的猜数字游戏:

// guessing/main.go
package main

import (
    "bufio"
    "fmt"
    "math/rand/v2" // Go 1.22+ 推荐 v2 版
    "os"
    "strconv"
    "strings"
)

const (
    MinNum    = 1
    MaxNum    = 100
    MaxTries  = 7
)

func main() {
    target := rand.IntN(MaxNum-MinNum+1) + MinNum
    fmt.Printf("我已经想好了一个 [%d, %d] 的数字,你有 %d 次机会猜中。\n",
        MinNum, MaxNum, MaxTries)

    reader := bufio.NewReader(os.Stdin)

    for tries := 1; tries <= MaxTries; tries++ {
        fmt.Printf("第 %d 次猜测,请输入: ", tries)
        line, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("读取输入出错:", err)
            return
        }
        line = strings.TrimSpace(line)

        guess, err := strconv.Atoi(line)
        if err != nil {
            fmt.Println("⚠️  请输入一个整数!")
            tries-- // 输入错误不消耗次数
            continue
        }

        switch {
        case guess < target:
            fmt.Println("🔼 偏小")
        case guess > target:
            fmt.Println("🔽 偏大")
        default:
            fmt.Printf("🎉 恭喜!你用 %d 次猜中了 %d。\n", tries, target)
            return
        }
    }
    fmt.Printf("😵 机会用尽,正确答案是 %d。\n", target)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

跑起来:

$ go mod init example.com/guessing
$ go run .
我已经想好了一个 [1, 100] 的数字,你有 7 次机会猜中。
第 1 次猜测,请输入: 50
🔽 偏大
第 2 次猜测,请输入: 25
🔼 偏小
...
1
2
3
4
5
6
7
8

这个例子用上了:

章节知识点 在哪一行体现
package main + func main() 文件开头
分组 import import (...)
const + iota 风格 const (...) 块
短变量声明 := target := ...
多返回值 + error line, err := reader.ReadString(...)
for 三段式 for tries := 1; ...
switch-true 模式 switch { case ... }
标准库 bufio / strconv / strings 多处

# 2.9 本章底层原理(简介)

关于 go mod init / go build 背后到底做了什么,留到卷三第 16 章「编译链接与演进」详讲。这里只需知道:

go run .
   │
   ├──► parse  解析 .go 源码 → AST
   ├──► typecheck  类型检查
   ├──► IR / SSA 中间代码
   ├──► 机器码生成
   ├──► link  链接为二进制(自动包含 runtime + GC)
   └──► 运行临时二进制 → 退出 → 删除二进制
1
2
3
4
5
6
7
8

整个流程在 几百毫秒内完成——这就是 Go "编译快"的直接体验。


# 2.10 Go 新手陷阱 Top 5

# ❌ 陷阱 1:import 写了但没用

import (
    "fmt"
    "strings" // 引入但代码里没用到
)

func main() {
    fmt.Println("hi")
}
1
2
3
4
5
6
7
8

编译错误:imported and not used: "strings"。

解决:删掉,或临时用 _ "strings" 副作用导入(仅当真的需要副作用时)。

这是 Go 故意为之的"零容忍"——避免依赖图臃肿。

# ❌ 陷阱 2:var x int = 0 冗余写法

// ❌ 冗余
var x int = 0
var s string = ""
var p *T = nil

// ✅ 简洁
var x int     // 零值就是 0
var s string  // 零值就是 ""
var p *T      // 零值就是 nil
1
2
3
4
5
6
7
8
9

golangci-lint 的 gosimple 会标红。

# ❌ 陷阱 3::= 在已有变量上误用

x := 1
{
    x := 2 // ❌ 这是新建一个 x(block-scoped),外层 x 还是 1
    fmt.Println(x) // 2
}
fmt.Println(x) // 1(被遮蔽 shadow 了)
1
2
3
4
5
6

多变量短声明的"半新半旧"陷阱:

err := doSomething()
if cond {
    val, err := doOther()  // ⚠️ 这里 err 是新变量!外层 err 不会被赋值
    _ = val
    _ = err
}
// 此处 err 仍是上面 doSomething 的结果
1
2
3
4
5
6
7

修复:把内层 := 改成 =,或把变量提前声明。

# ❌ 陷阱 4:go run main.go 而不是 go run .

# 项目有多个 .go 文件时
go run main.go  # ❌ 找不到其他文件中的符号
go run .        # ✅ 编译当前目录所有 .go
1
2
3

记牢:永远 go run . / go build .,让目录而非单文件做编译单元。

# ❌ 陷阱 5:把 go mod tidy 当成"普通命令"乱跑

go mod tidy 会:

  • 添加 go.mod 中缺失的、代码里 import 的依赖
  • 删除 go.mod 中存在但代码里没 import 的依赖

如果你在大型项目里跑了一半就停,或者在 build tag 之外的代码里没 import 某个包,tidy 会把它从 go.mod 删掉。重要项目改 go.mod 前,先 commit,再跑 go mod tidy,diff 看清楚再提交。


# 2.11 思考题

  1. 为什么 Go 把 import 没用变成编译错误,而不是警告?这种"零容忍"的设计代价是什么?
  2. package main 与 package mylib 的二进制产物有什么区别?哪个能被 go build 直接生成可执行文件?
  3. 如果一个常量 const N = 1 << 100 你声明了但从来不用,编译能通过吗?为什么 Go 对未使用的常量没有报错?
  4. 写一段代码,使两个不同包路径的包都名为 rand,并能在同一个文件里使用。
  5. iota 在嵌套 const 块里会怎么计数?请写一段代码验证你的猜测。
  6. 把 2.8 节的猜数字游戏改造,让它读取命令行参数指定区间(go run . 1 1000)。提示:用 os.Args + strconv.Atoi。

# 2.12 推荐阅读

# 卷内

  • 卷一第 3 章 数据类型
  • 卷一第 17 章 工程化与模块(深入 go.mod / MVS)

# 跨卷

  • 卷三第 16 章 编译链接与演进(go build 底层)
  • 卷四第 12 章 编译链接原理(实战瘦身)

# 外部资料

  • Go 官方安装指南 (opens new window)
  • Effective Go: Names (opens new window)
  • Tour of Go (opens new window)(官方互动入门)
  • Go Modules Reference (opens new window)
上次更新: 2026/06/10, 11:13:41
Go简史
数据类型

← Go简史 数据类型→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式