4. Go 基础语法速览:变量与常量、基本数据类型、控制结构、函数与错误处理

在开始构建 Web 服务之前,掌握 Go 的核心语法是高效开发的基础。本章将快速梳理变量与常量、基本数据类型、三大控制结构(if、for、switch)以及函数与错误处理机制。这些知识点将贯穿整个课程,是编写可靠、可维护 Go 代码的基石。

4.1 变量与常量

Go 语言是静态类型语言,变量声明后类型不可变。Go 提供了多种声明方式以适应不同场景,同时通过 const 关键字支持编译时常量。

4.1.1 变量声明

声明方式 语法示例 适用场景
标准声明 var name string = "Go" 需要显式指定类型或初始化值
类型推断 var port = 8080 编译器根据初始值自动推导类型
短变量声明 msg := "Hello" 函数内部声明并初始化,最常用
批量声明 var ( x int; y string ) 声明多个相关变量,提高可读性
💡 关键点: 短变量声明 := 只能在函数内部使用;包级别变量必须使用 var 关键字。未使用的局部变量会导致编译错误。

4.1.2 常量与 iota

常量使用 const 声明,值在编译期确定。Go 提供了 iota 枚举生成器,用于创建一组递增的常量。

const (
    StatusOK       = 200
    StatusNotFound = 404
)

// iota 示例:定义日志级别
const (
    DebugLevel = iota // 0
    InfoLevel         // 1
    WarnLevel         // 2
    ErrorLevel        // 3
)
💡 提示: iota 在每次出现 const 关键字时重置为 0,常用于枚举、位掩码或状态码定义。

4.2 基本数据类型

Go 的基本数据类型分为四大类:布尔型、数值型、字符串型和派生类型。下表列出了 Web 开发中最常用的类型。

类别 类型 说明 默认值
布尔 bool true 或 false false
整型 int, int8 ~ int64 平台相关(32/64位)或定长 0
无符号整型 uint, uint8 ~ uint64 非负整数,常用于位运算 0
浮点型 float32, float64 Web 开发中优先使用 float64 0.0
字符串 string UTF-8 编码,不可变字节序列 ""
字节 byte uint8 的别名,处理原始数据 0
符文 rune int32 的别名,表示 Unicode 码点 0
⚠️ 注意: Go 没有隐式类型转换。不同数值类型之间必须显式转换,例如 int64(port)。字符串与数值之间需使用 strconv 包。

4.3 控制结构:if、for、switch

Go 的控制结构简洁且无括号包裹条件,所有控制语句都支持在条件前执行一个简单语句(如变量声明)。

4.3.1 if 语句

// 标准 if-else
if statusCode >= 400 {
    log.Println("请求失败")
} else if statusCode >= 300 {
    log.Println("重定向")
} else {
    log.Println("成功")
}

// 带初始化语句的 if(作用域仅限于 if 块)
if err := validateRequest(r); err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}

4.3.2 for 循环

Go 只有 for 关键字,但能实现所有循环模式。

模式 语法 示例
标准 for for init; condition; post { } for i := 0; i < 10; i++ { }
条件 for(类似 while) for condition { } for retries > 0 { }
无限循环 for { } for { select {} } 用于常驻服务
range 遍历 for index, value := range collection { } 遍历切片、map、字符串、通道
// range 遍历 HTTP 请求头
for key, values := range r.Header {
    for _, v := range values {
        log.Printf("Header %s: %s", key, v)
    }
}

4.3.3 switch 语句

Go 的 switch 默认每个 case 后自动 break,无需显式写 break。支持表达式、类型判断和 fallthrough。

// 表达式 switch
switch method := r.Method; method {
case http.MethodGet:
    handleGet(w, r)
case http.MethodPost:
    handlePost(w, r)
case http.MethodPut, http.MethodPatch:
    handleUpdate(w, r)
default:
    http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}

// 类型 switch(用于接口类型断言)
var data interface{} = "hello"
switch v := data.(type) {
case string:
    fmt.Println("字符串长度:", len(v))
case int:
    fmt.Println("整数值:", v)
default:
    fmt.Println("未知类型")
}
💡 提示: 在 Web 路由处理中,switch 比 if-else 链更清晰。类型 switch 是处理多类型接口值的标准方式。
Go 控制结构执行流程 程序入口 if condition { ... } for init; condition; post { ... } switch value { case ... } 程序结束 / 返回 说明:Go 控制结构按顺序执行,每个结构都支持在条件前添加初始化语句(如 if err := fn(); err != nil { }) switch 默认每个 case 自动 break,无需显式 break 语句

4.4 函数与错误处理

Go 函数支持多返回值,这是其错误处理机制的核心。函数是一等公民,可以作为参数传递或作为返回值返回。

4.4.1 函数定义

// 标准函数:多返回值
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除数不能为0")
    }
    return a / b, nil
}

// 命名返回值(在函数体内直接赋值,return 时自动返回)
func parseConfig(path string) (cfg *Config, err error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("读取配置文件失败: %w", err)
    }
    cfg = &Config{}
    err = json.Unmarshal(data, cfg)
    return
}

// 可变参数
func logRequest(method string, headers ...string) {
    fmt.Printf("Method: %s\n", method)
    for _, h := range headers {
        fmt.Printf("Header: %s\n", h)
    }
}

4.4.2 错误处理惯用法

Go 没有异常机制,错误通过返回值传递。调用者必须显式检查错误,这是 Go 代码健壮性的来源。

模式 代码示例 说明
基本检查 if err != nil { return err } 最常用,立即返回错误
错误包装 fmt.Errorf("上下文: %w", err) 使用 %w 保留原始错误链
defer + recover defer func() { if r := recover(); r != nil { } }() 捕获 panic,仅用于不可恢复场景
自定义错误 type MyError struct { Code int; Msg string } 实现 Error() string 接口
// Web 服务中的典型错误处理模式
func handler(w http.ResponseWriter, r *http.Request) {
    user, err := getUserFromDB(r.Context(), r.URL.Query().Get("id"))
    if err != nil {
        // 包装错误并返回 HTTP 500
        log.Printf("获取用户失败: %v", err)
        http.Error(w, "内部服务器错误", http.StatusInternalServerError)
        return
    }
    // 正常处理
    json.NewEncoder(w).Encode(user)
}
⚠️ 注意: 永远不要忽略错误。如果函数返回 error,调用者必须处理它。使用 _ 忽略错误仅适用于明确知道不会出错的情况(如 fmt.Fprint 写入 bytes.Buffer)。
Go 错误处理流程 调用函数:result, err := fn() if err != nil ? 是 错误处理 log.Printf("错误: %v", err) return err / http.Error 否 正常处理 使用 result 继续 json.Encode / 业务逻辑 函数返回 说明:Go 强制检查错误,推荐使用 fmt.Errorf("%w") 包装错误以保留错误链,defer+recover 仅用于 panic 恢复

4.5 综合示例:Web 处理函数中的语法应用

以下示例将本章所有语法点融合到一个简单的 HTTP 处理函数中,展示实际开发中的写法。

package main

import (
    "encoding/json"
    "errors"
    "log"
    "net/http"
    "strconv"
)

// User 结构体(后续章节详解)
type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

// 模拟数据库查询
func findUserByID(id int) (*User, error) {
    if id <= 0 {
        return nil, errors.New("无效的用户ID")
    }
    // 模拟数据
    return &User{ID: id, Name: "Alice"}, nil
}

// getUserHandler 是典型的 Web 处理函数
func getUserHandler(w http.ResponseWriter, r *http.Request) {
    // 1. 解析查询参数
    idStr := r.URL.Query().Get("id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "参数 id 必须是整数", http.StatusBadRequest)
        return
    }

    // 2. 调用业务逻辑
    user, err := findUserByID(id)
    if err != nil {
        log.Printf("查询用户失败: %v", err)
        http.Error(w, "用户未找到", http.StatusNotFound)
        return
    }

    // 3. 设置响应头并返回 JSON
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(user)
}

func main() {
    http.HandleFunc("/user", getUserHandler)
    log.Println("服务启动在 :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
💡 提示: 上述代码展示了变量声明(:=)、基本类型转换(strconv.Atoi)、if 错误检查、函数多返回值、常量状态码(http.StatusBadRequest)等核心语法。这是 Go Web 开发的标准模式。