7.1 KiB
7.1 KiB
第 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 动手练习
- 编写
fib(n int) int递归与迭代两版。 - 定义
User结构体,实现String() string方法。 - 模仿
pkg/response封装Result,编写Success/Fail并用encoding/json序列化验证。
下一章将学习并发与工程化。