go学习
数组和切片定义对比
⏺ Go 中数组与切片的声明
字符串
// 数组(固定长度)
var a1 [3]string // 零值: [“”, “”, “”]
a2 := [3]string{“a”, “b”, “c”} // 字面量初始化
a3 := […]string{“x”, “y”, “z”} // 编译器推断长度
// 切片(动态长度)
var s1 []string // 零值: nil
s2 := []string{“a”, “b”, “c”} // 字面量初始化
s3 := make([]string, 3) // 长度 3,容量 3
s4 := make([]string, 0, 10) // 长度 0,容量 10
int
// 数组(固定长度)
var a1 [3]int // 零值: [0, 0, 0]
a2 := [3]int{1, 2, 3}
a3 := […]int{10, 20, 30}
// 切片(动态长度)
var s1 []int // 零值: nil
s2 := []int{1, 2, 3}
s3 := make([]int, 3)
s4 := make([]int, 0, 10)
核心区别
| 数组 [N]T | 切片 []T | |
|---|---|---|
| 长度 | 固定,编译时确定 | 可变,运行时增长 |
| 声明 | [3]int — 带数字 | []int — 不带数字 |
| 零值 | 元素为类型零值 | nil |
| 赋值/传参 | 值拷贝(整个复制) | 引用传递(共享底层数组) |
| 能否 append | ❌ 不行 | ✅ append(s, v) |
总结:方括号里有东西(数字或 …)就是数组,空的 [] 才是切片
全局变量
var c, m []interface{}必须要使用var声明。
- 使用方式:
var name Type或var name = value;不能用 var name Type{}(这是局部变量的短声明风格) []interface{}:Go 的”万能容器”,可存放任意类型,等价于[]any- 使用示例:
1 | // 方案1:用 = 赋值(推荐!) |
- 推荐使用方法 1,该方法不存在空指针!
[]any
[]any是一个数据类型(不能单独作为值)
any 是 Go 1.18+ 中 interface{} 的别名(完全等价)
- 花括号 {} 创建结构体实例
切片字面量
- 切片字面量必须写成 []any{元素} 或 []any{}(空切片),即切片字面量必须包含
{} - 可以使用切片自变量
{}的数据类型包括:- 切片
- 数组
- Map
- 结构体
1 | // 切片 |
使用示例:
1 | b := []any{} // ✅ 空切片 |
语法规则:
| 场景 | 允许的语法 | 禁止的语法 | 原因 |
|---|---|---|---|
| 全局变量 | var x = []T{} |
var x []T{} |
语法规则限制 |
var x []T (零值) |
x := []T{} |
:= 不能在全局使用 |
|
| 局部变量 | x := []T{} |
var x []T{} |
局部声明不支持此语法 |
var x = []T{} |
x := []T |
缺少 {} |
|
| 函数参数/返回值 | func f(a []T) |
func f(a []T{}) |
{} 只用于字面量 |
诀窍:
1 | 写切片,带花括号; |
局部变量声明方式
| 方式 | 代码示例 | 说明 | 使用场景 |
|---|---|---|---|
| 方式1:var + 类型 | var param dto.CreateProjectRequest |
声明变量,自动初始化为零值 | ✅ 最常用,清晰明确 |
| 方式2:var + 类型 + 赋值 | var param dto.CreateProjectRequest = dto.CreateProjectRequest{} |
声明并显式初始化 | 需要明确初始化时 |
| 方式3:短变量声明 | param := dto.CreateProjectRequest{} |
简洁的声明+初始化 | ✅ 函数内部常用 |
| 方式4:var + 推断类型 | var param = dto.CreateProjectRequest{} |
自动推断类型 | 类型明显时 |
方式一构建的变量,每种类型都有默认的零值。
零值规则:
| 类型 | 零值 | 示例 |
|---|---|---|
| 整数(int, int64等) | 0 | var age int → age = 0 |
| 浮点数(float64等) | 0.0 | var price float64 → price = 0.0 |
| 字符串(string) | “” (空字符串) | var name string → name = "" |
| 布尔值(bool) | false | var isActive bool → isActive = false |
| 指针、切片、映射、通道、函数、接口 | nil | var ptr *int → ptr = nil |
| 结构体 | 所有字段都是零值 | var param dto.CreateProjectRequest → 所有字段为零值 |
1 | var res int |
for 循环
Go 没有 while 关键字,全部用 for 搞定!
1️⃣ 经典三段式(类似 C/Java)
1 | for i := 0; i < 10; i++ { |
2️⃣ 只有条件(类似 while)
1 | i := 0 |
3️⃣ 无限循环(你代码的写法)
1 | for { |
4️⃣ range 遍历
1 | nums := []int{1, 2, 3} |
| 写法 | 含义 |
|---|---|
for i := 0; i < n; i++ |
经典循环 |
for 条件 |
类似 while |
for {} |
无限循环,靠 break/return 退出 |
for range |
遍历切片/map/字符串 |
append(c, m…)
- m…:展开操作符,把 m 切片拆成独立元素,加入到c中。
- append 之后不改变 c,返回一个新的对象
运行代码
方式一:go build - 编译生成可执行文件
1 | // 编译 |
📦 编译:将 main.go 及其依赖的所有 Go 代码编译成机器码
💾 生成文件:创建一个名为 robot_train_platform 的可执行文件
⚡ 可独立运行:生成的文件可以直接运行,不需要 Go 环境
方式二:go run - 临时编译并运行
1 | go run main.go |
🔄 临时编译:在临时目录编译代码
🚀 立即运行:编译完成后立即执行
🗑️ 自动清理:运行结束后自动删除临时文件
💡 开发友好:适合开发阶段快速测试
命名返回值
- 在函数返回值中确定变量名:(rsp any, err error)
在 Go 中,你可以给返回值命名,而不只是声明类型。这样做的效果是:
- 自动声明变量:
rsp和err在函数体内被自动声明为局部变量。 - 零值初始化:它们会被初始化为各自类型的零值。
- rsp any → 零值是 nil
- err error → 零值是 nil
1 | func (s *RobotService) RegisterRobot(ctx context.Context, param dto.RegisterRobotReq) (rsp any, err error) { |
否则上面的代码要这样写:
1 | // 写法2:普通返回值(需要手动声明) |
Go 语言的包函数命名规则
例如一个工具包 tool/uuid.go 实现如下:
1 | package tool // ← 声明这个文件属于 tool 包 |
由于 UUID() 首字母大写了,所以可以被其他包引用使用,方法如下:tool.UUID()
Go 语言测试
测试要求:
| 规则 | 说明 | 示例 |
|---|---|---|
| 文件名 | 必须以 _test.go 结尾 |
param_test.go ✅param.go ❌ |
| 包名 | 与被测试文件相同 | package tool |
| 函数名 | 必须以 Test 开头 |
TestValidateRegex ✅validateRegex ❌ |
| 参数 | 必须是 *testing.T |
func TestXxx(t *testing.T) |
param_test.go 示例代码:
1 | package tool |
使用方法:
1 | cd ./logic/tool |
常见的 t *testing.T 输出:
| 方法 | 说明 | 示例 |
|---|---|---|
| t.Error(args) | 报告错误,但继续执行 | t.Error("验证失败") |
| t.Errorf(format, args) | 格式化报告错误 | t.Errorf("期望 %v,得到 %v", expected, actual) |
| t.Fatal(args) | 报告错误并立即停止测试 | t.Fatal("严重错误") |
| t.Fatalf(format, args) | 格式化报告错误并停止 | t.Fatalf("无法连接数据库: %v", err) |
| t.Log(args) | 记录日志(只在失败时显示) | t.Log("测试开始") |
| t.Logf(format, args) | 格式化记录日志 | t.Logf("测试数据: %+v", data) |
| t.Skip(args) | 跳过测试 | t.Skip("暂时跳过") |
| t.Parallel() | 标记测试可以并行运行 | t.Parallel() |
fmt.Sprintf
数据格式化
1 | // %d - 十进制整数 |
ctx
1 | func ExampleContextValue() { |
完整代码
1 | package main |