3. Go 基础语法回顾:变量与常量、数据类型、控制结构、函数与错误处理
在开始构建 REST API 之前,扎实的 Go 语言基础是高效开发与调试的保障。本章将系统回顾 Go 的核心语法要素,重点聚焦于变量与常量的声明、数据类型体系、控制结构、函数定义以及 Go 独特的错误处理模式。这些知识点将贯穿后续的接口开发与测试环节。
3.1 变量与常量
Go 语言采用静态类型系统,变量声明方式灵活,支持类型推断。常量则使用 const 关键字定义,编译期确定值。
变量声明
| 声明方式 | 语法示例 | 说明 |
|---|---|---|
| 标准声明 | var name string = "Go" |
显式指定类型与初始值 |
| 类型推断 | var version = 1.21 |
编译器根据初始值推导类型 |
| 短变量声明 | count := 10 |
函数内使用,省略 var 与类型 |
| 批量声明 | var ( x int; y string ) |
使用 var 块组织多个变量 |
最佳实践: 在函数内部优先使用短变量声明
:=;包级别变量使用 var 块保持可读性。
常量与 iota
常量使用 const 声明,支持 iota 枚举生成器,常用于定义一组递增的整型常量。
const (
StatusPending = iota // 0
StatusActive // 1
StatusInactive // 2
StatusDeleted // 3
)
const Pi = 3.14159
3.2 数据类型体系
Go 的数据类型分为基础类型、复合类型与引用类型。理解类型的内存布局与零值机制,是避免空指针和类型转换错误的关键。
基础类型概览
| 类别 | 类型 | 零值 | 典型用途 |
|---|---|---|---|
| 布尔 | bool |
false |
条件判断、开关标志 |
| 整数 | int, int8 ~ int64, uint |
0 |
计数、索引、状态码 |
| 浮点 | float32, float64 |
0.0 |
百分比、坐标、精度计算 |
| 字符串 | string |
"" |
请求体、路径参数、错误消息 |
| 字节 | byte (alias for uint8) |
0 |
原始数据、IO 缓冲区 |
复合类型:数组与切片
数组长度固定,切片则是对底层数组的动态视图,是 Go 中最常用的序列类型。
// 数组:长度固定
var arr [3]int = [3]int{1, 2, 3}
// 切片:动态长度
slice := []int{1, 2, 3}
slice = append(slice, 4) // 追加元素
常见陷阱: 切片是引用类型,函数传参时共享底层数组;修改切片元素会影响原调用方。若需隔离副本,使用
copy() 函数。
复合类型:结构体与映射
结构体用于聚合不同字段,映射提供键值对查找,两者在 API 数据建模中频繁使用。
// 结构体定义
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
// 映射声明与初始化
headers := map[string]string{
"Content-Type": "application/json",
}
3.3 控制结构
Go 的控制结构简洁,仅保留 if、for、switch,去掉了 while 和 do-while。所有控制语句的条件表达式不需要括号包裹。
条件分支
// if 支持初始化语句
if err := validate(req); err != nil {
return err
}
// switch 默认带 break,无需显式写
switch method {
case "GET":
handleGet()
case "POST":
handlePost()
default:
http.Error(w, "Method not allowed", 405)
}
循环结构
Go 只有 for 关键字,但可以模拟多种循环模式。
| 模式 | 示例 | 说明 |
|---|---|---|
| 标准 for | for i := 0; i < n; i++ { } |
初始化、条件、后置语句 |
| 类似 while | for n > 0 { n-- } |
仅保留条件部分 |
| 无限循环 | for { select {} } |
常用于服务器主循环 |
| range 迭代 | for k, v := range map { } |
遍历切片、映射、通道 |
提示: 使用
for range 遍历切片时,迭代变量是副本;若需修改原元素,应通过索引访问。
3.4 函数与错误处理
函数是 Go 代码组织的基本单元。Go 支持多返回值,且将错误作为返回值显式传递,这是其错误处理哲学的核心。
函数定义与多返回值
// 多返回值:正常结果 + 错误
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// 命名返回值:自动初始化零值,return 时无需指定变量
func ParseID(s string) (id int, err error) {
id, err = strconv.Atoi(s)
return
}
错误处理模式
Go 没有异常机制,错误通过 error 接口值传递。调用者必须显式检查错误,避免忽略潜在故障。
result, err := Divide(10, 0)
if err != nil {
log.Printf("计算失败: %v", err)
return
}
fmt.Println("结果:", result)
核心原则: 永远不要忽略错误。即使你认为某个函数不会出错,也应至少使用
_ 占位或记录日志。在 REST API 中,未处理的错误可能导致 500 响应或数据泄露。
defer 延迟执行
defer 用于注册延迟调用,在函数返回前执行,常用于资源清理、关闭连接、解锁等操作。
func ReadFile(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close() // 确保文件关闭
return io.ReadAll(f)
}
3.5 语法要素关系图
下图展示了 Go 基础语法要素之间的组织关系,帮助理解变量、类型、控制流与函数如何协同构成程序骨架。
3.6 常见错误模式与避坑指南
在 REST API 开发中,以下语法层面的错误模式频繁出现,提前识别可大幅减少调试时间。
| 错误模式 | 示例 | 正确做法 |
|---|---|---|
| 未使用的变量 | var x int 但未使用 |
使用 _ 占位或删除 |
| 切片传参误修改 | 函数内 s[0]=99 影响外部 |
使用 copy() 创建副本 |
| 忽略错误返回值 | json.Decode(r, &v) 不检查 err |
始终 if err != nil { return } |
| defer 在循环内使用 | for { f, _ := os.Open(); defer f.Close() } |
将文件操作封装为独立函数 |
| map 未初始化直接写入 | var m map[string]int; m["k"]=1 |
使用 make() 初始化 |
开发效率提示: 使用
go vet 静态分析工具可以在编译前发现未使用变量、错误检查遗漏等问题。建议在 CI 流程中集成 go vet 检查。
3.7 函数与错误处理流程
下图展示了 Go 函数调用中错误处理的典型流程,强调“尽早返回错误”的设计模式。