全局变量
var c, m []interface{}必须要使用var声明。
- 使用方式:
var name Type 或 var name = value;不能用 var name Type{}(这是局部变量的短声明风格)
[]interface{}:Go 的”万能容器”,可存放任意类型,等价于[]any
- 使用示例:
1 2 3 4 5 6 7 8
| var define_1 = []any{}
var define_1 []any func init() { define_1 = []any{} }
|
[]any
any 是 Go 1.18+ 中 interface{} 的别名(完全等价)
切片字面量
- 切片字面量必须写成 []any{元素} 或 []any{}(空切片),即切片字面量必须包含
{}
- 可以使用切片自变量
{}的数据类型包括:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| s := []int{} s := []int{1, 2, 3}
arr := [3]int{} arr := [3]int{1, 2, 3}
m := map[string]int{} m := map[string]int{"a": 1}
type Person struct { Name string Age int } p := Person{} p := Person{Name: "张三", Age: 20}
|
使用示例:
1 2
| b := []any{} b := []any{1, "two"}
|
语法规则:
| 场景 |
允许的语法 |
禁止的语法 |
原因 |
| 全局变量 |
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 2 3 4
| 写切片,带花括号; 无 {},必报错; 全局用=,局部用:=; 混合类型用 any!”
|
局部变量声明方式
| 方式 |
代码示例 |
说明 |
使用场景 |
| 方式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 2
| var res int var arr []int
|
append(c, m…)
- m…:展开操作符,把 m 切片拆成独立元素,加入到c中。
- append 之后不改变 c,返回一个新的对象
运行代码
方式一:go build - 编译生成可执行文件
1 2 3 4
| // 编译 go build -o robot_train_platform main.go // 运行 ./robot_train_platform
|
📦 编译:将 main.go 及其依赖的所有 Go 代码编译成机器码
💾 生成文件:创建一个名为 robot_train_platform 的可执行文件
⚡ 可独立运行:生成的文件可以直接运行,不需要 Go 环境
方式二:go run - 临时编译并运行
🔄 临时编译:在临时目录编译代码
🚀 立即运行:编译完成后立即执行
🗑️ 自动清理:运行结束后自动删除临时文件
💡 开发友好:适合开发阶段快速测试
命名返回值
- 在函数返回值中确定变量名:(rsp any, err error)
在 Go 中,你可以给返回值命名,而不只是声明类型。这样做的效果是:
- 自动声明变量:
rsp 和 err 在函数体内被自动声明为局部变量。
- 零值初始化:它们会被初始化为各自类型的零值。
- rsp any → 零值是 nil
- err error → 零值是 nil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| func (s *RobotService) RegisterRobot(ctx context.Context, param dto.RegisterRobotReq) (rsp any, err error) { defer func() { if r := recover(); r != nil { stack := debug.Stack() ilog.CtxErrorf(ctx, "[RegisterRobot] panic: %v, stack: %s", r, string(stack)) err = errors.New("panic occur") } }()
ilog.CtxInfof(ctx, "[RegisterRobot] param: %s", tool.JsonifyIndent(param))
uin := tool.UinStr(ctx) ownerUin := tool.OwnerUinStr(ctx)
if err := s.validateRegisterParams(ctx, param, ownerUin); err != nil { return rsp, err } ... }
|
否则上面的代码要这样写:
1 2 3 4 5 6 7
| func (s *RobotService) RegisterRobot(...) (any, error) { var rsp any if err := someFunc(); err != nil { return rsp, err } }
|
Go 语言的包函数命名规则
例如一个工具包 tool/uuid.go 实现如下:
1 2 3 4 5 6 7 8
| package tool
import "github.com/google/uuid"
func UUID() string { return uuid.New().String() }
|
由于 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package tool
import ( "fmt" "testing" )
func TestValidateRegex(t *testing.T) { str := "my-robot-01" if !ValidateRegex("^[a-zA-Z0-9:_-]{1,48}$", str) { fmt.Println("invalid") } }
|
使用方法:
1 2
| cd ./logic/tool go test -v -run TestValidateRegex
|
常见的 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| fmt.Sprintf("%d", 123)
fmt.Sprintf("%s", "hello")
fmt.Sprintf("%v", 123) fmt.Sprintf("%v", "hello")
type User struct { Name string Age int } user := User{Name: "Alice", Age: 30} fmt.Sprintf("%+v", user)
fmt.Sprintf("%#v", user)
fmt.Sprintf("%T", 123) fmt.Sprintf("%T", "hello")
fmt.Sprintf("%t", true)
fmt.Sprintf("%.2f", 3.14159)
ptr := &user fmt.Sprintf("%p", ptr)
|
ctx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| func ExampleContextValue() { ctx := context.Background() ctx = context.WithValue(ctx, "key1", "value1") ctx = context.WithValue(ctx, "key2", "value2") ctx = context.WithValue(ctx, "key3", "value3") fmt.Println(ctx.Value("key1")) fmt.Println(ctx.Value("key2")) fmt.Println(ctx.Value("key3")) fmt.Println(ctx.Value("key4")) }
|
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package main
import ( "fmt" )
var zes []any
var initializedGlobal = []any{"global_item"}
func main() { fmt.Println("zes (nil 切片):", zes == nil) zes = []any{} fmt.Println("append 到 nil 切片:", append(zes, 1))
a := []any{1, 2, 3} b := []any{} fmt.Println("b (空切片):", b)
c := []interface{}{1, 2, 3} m := []interface{}{}
fmt.Printf("a 的值: %v, 类型: %T\n", a, a) fmt.Printf("c 的值: %v\n", c) fmt.Printf("m 长度: %d\n", len(m)) fmt.Printf("m 类型: %T\n", m) }
|