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

7.1 KiB
Raw Permalink Blame History

第 1 章 Go 环境与基础语法

学完本章你将:装好 Go、理解 go mod、掌握变量/类型/流程/函数/结构体/接口/错误处理,能读懂本项目 90% 的非并发代码。

1.1 安装与环境

安装

  • 官网 https://go.dev/dl 下载对应系统安装包
  • 验证:go version 应输出 go1.26.x
  • 本项目要求 go 1.26.2(见 go.mod 第一行)

环境变量

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 开头:

module streambox
go 1.26.2

常用命令(对应本项目 Makefile):

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:

package main

import "fmt"

func main() {
    fmt.Println("hello, streambox")
}

运行 go run main.go。package main + func main() 是可执行程序入口。

1.4 变量与常量

// 显式声明
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 码点 '中'

字符串操作:

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

// 数组(定长,很少直接用)
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 流程控制

// 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 函数

// 普通函数
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 结构体与方法

// 定义
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 多态的核心,隐式实现:

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 无异常,错误即值:

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 序列化验证。

下一章将学习并发与工程化。