# 第 1 章 Go 环境与基础语法 > 学完本章你将:装好 Go、理解 go mod、掌握变量/类型/流程/函数/结构体/接口/错误处理,能读懂本项目 90% 的非并发代码。 ## 1.1 安装与环境 ### 安装 - 官网 https://go.dev/dl 下载对应系统安装包 - 验证:`go version` 应输出 `go1.26.x` - 本项目要求 `go 1.26.2`(见 `go.mod` 第一行) ### 环境变量 ```bash go env GOPATH GOROOT GOPROXY # GOPATH Workspace 目录(Go 1.11 后已弱化,保留兼容) # GOROOT Go 安装目录 # GOPROXY 代理,国内建议: go env -w GOPROXY=https://goproxy.cn,direct go env -w GOPRIVATE="" ``` ### 编辑器 推荐 VS Code + Go 插件(gopls),或 GoLand。安装后执行 `go install golang.org/x/tools/gopls@latest`。 ## 1.2 go mod 模块管理 Go 1.11+ 用 `go.mod` 管理依赖,替代 GOPATH。本项目 `go.mod` 开头: ```go module streambox go 1.26.2 ``` 常用命令(对应本项目 `Makefile`): ```bash go mod init streambox # 初始化(已存在无需重复) go mod tidy # 整理依赖,对应 make tidy go get github.com/gin-gonic/gin@latest # 添加/升级依赖 go list -m all # 查看所有依赖 go mod why github.com/gin-gonic/gin # 查询谁依赖它 ``` `go.sum` 是依赖校验和,提交到 Git,不要手改。 ## 1.3 Hello World 创建 `main.go`: ```go package main import "fmt" func main() { fmt.Println("hello, streambox") } ``` 运行 `go run main.go`。`package main` + `func main()` 是可执行程序入口。 ## 1.4 变量与常量 ```go // 显式声明 var name string = "streambox" var port int = 8080 var debug bool // 类型推导 var addr = ":8080" // 短声明(函数内常用) count := 42 msg := "ok" // 批量声明 var ( host = "0.0.0.0" timeout = 10 ) // 常量 const AppName = "streambox" const ( ModeDebug = "debug" ModeRelease = "release" ) // iota 枚举 const ( CodeSuccess = iota // 0 CodeBadRequest // 1 CodeInternal // 2 ) ``` 零值:`int` 为 0,`string` 为 "",`bool` 为 false,指针/slice/map/channel/interface 为 nil。 ## 1.5 基础类型 | 类型 | 说明 | 示例 | |------|------|------| | `bool` | 布尔 | `true/false` | | `int/int8/int16/int32/int64` | 有符号整型 | `int` 随平台 32/64 位 | | `uint/...` | 无符号整型 | `uint port = 8080` | | `float32/float64` | 浮点 | `float64` 常用 | | `string` | 字符串,不可变,UTF-8 | `"hello"` | | `byte` (alias uint8) | 字节 | `'a'` | | `rune` (alias int32) | Unicode 码点 | `'中'` | 字符串操作: ```go s := "hello" len(s) // 字节长度 5 s[1] // byte 'e' for _, r := range s { // 按 rune 遍历 fmt.Printf("%c", r) } s + " world" // 拼接 fmt.Sprintf("port=%d", 8080) ``` ## 1.6 复合类型:数组、切片、Map ```go // 数组(定长,很少直接用) var a [3]int = [3]int{1,2,3} // 切片(动态数组,最常用) s := []int{1, 2, 3} s = append(s, 4) sub := s[1:3] // [2,3] 左闭右开 s2 := make([]int, 0, 8) // len 0 cap 8 // Map m := map[string]int{"a": 1} m["b"] = 2 v, ok := m["c"] // ok 判断是否存在 delete(m, "a") for k, v := range m { fmt.Println(k, v) } m2 := make(map[string]string) ``` 注意:slice/map 作为参数传递时是引用语义;nil slice 可 append,nil map 不可写入。 ## 1.7 流程控制 ```go // if if err := do(); err != nil { log.Fatal(err) } else { fmt.Println("ok") } // for 只有 for for i := 0; i < 3; i++ {} for range s {} for { break } // 死循环 // switch switch mode { case "debug": fmt.Println("debug") case "release": fmt.Println("release") default: fmt.Println("unknown") } // switch 带表达式 switch { case port < 1024: fmt.Println("privileged") default: fmt.Println("ok") } // defer 延迟执行(LIFO),常用于资源释放 f, _ := os.Open("config.yaml") defer f.Close() defer logger.Sync() // 本项目 main.go 中就有 ``` ## 1.8 函数 ```go // 普通函数 func Add(a, b int) int { return a + b } // 多返回值(Go 错误处理的核心) func Divide(a, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("divide by zero") } return a / b, nil } // 命名返回值 func Split(s string) (first, second string) { // ... return } // 可变参数 func Sum(nums ...int) int { n := 0 for _, v := range nums { n += v } return n } // 函数作为值 fn := func(s string) string { return s + "!" } type HandlerFunc func(*gin.Context) // Gin 中大量使用 ``` ## 1.9 结构体与方法 ```go // 定义 type ServerConfig struct { Port int `yaml:"port" mapstructure:"port"` Mode string `yaml:"mode"` ReadTimeout string `yaml:"read_timeout"` WriteTimeout string `yaml:"write_timeout"` } // 方法(接收者为值或指针) func (c ServerConfig) Addr() string { return fmt.Sprintf(":%d", c.Port) } func (c *ServerConfig) SetPort(p int) { c.Port = p } // 初始化 cfg := ServerConfig{Port: 8080, Mode: "debug"} cfg2 := &ServerConfig{Port: 8080} // 指针 // 嵌入(组合,非继承) type Config struct { Server ServerConfig `mapstructure:"server"` Log LogConfig } ``` 本项目 `config/config.go` 就是典型的结构体 + viper 绑定。 ## 1.10 接口 接口是 Go 多态的核心,隐式实现: ```go type Pinger interface { Ping() string } // 只要实现了 Ping() string,就实现了 Pinger,无需显式声明 type PingService struct{} func (s *PingService) Ping() string { return "pong" } var _ Pinger = (*PingService)(nil) // 编译期校验 // 空接口 interface{}(Go 1.18 后别名 any)可表示任意类型 var x any = 42 x = "hello" // 类型断言 if s, ok := x.(string); ok { fmt.Println(s) } // type switch switch v := x.(type) { case string: fmt.Println("string", v) case int: fmt.Println("int", v) } ``` Gin 的 `gin.HandlerFunc` 本质是 `func(*gin.Context)`,也是一种函数类型接口。 ## 1.11 错误处理 Go 无异常,错误即值: ```go f, err := os.Open("config.yaml") if err != nil { return fmt.Errorf("open config: %w", err) // %w 包装,支持 errors.Is/As } defer f.Close() // 自定义错误 type AppError struct { Code int Msg string } func (e *AppError) Error() string { return e.Msg } // 判断 if errors.Is(err, os.ErrNotExist) { ... } var appErr *AppError if errors.As(err, &appErr) { fmt.Println(appErr.Code) } // panic/recover 仅用于不可恢复错误,业务错误不要用 // 本项目 middleware/recovery.go 用 recover 捕获 panic,避免进程崩溃 ``` ## 1.12 包与可见性 - 同目录同 `package` 名 - 大写开头导出(Public),小写私有:`logger.Log` 可导出,`join` 不可(见 `middleware/cors.go`) - 导入:`import "streambox/pkg/logger"`,`import gin "github.com/gin-gonic/gin"` ## 1.13 动手练习 1. 编写 `fib(n int) int` 递归与迭代两版。 2. 定义 `User` 结构体,实现 `String() string` 方法。 3. 模仿 `pkg/response` 封装 `Result`,编写 `Success`/`Fail` 并用 `encoding/json` 序列化验证。 下一章将学习并发与工程化。