StreamBox/docs/01-go-环境与基础语法.md

324 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 第 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` 序列化验证。
下一章将学习并发与工程化。