全局变量

var c, m []interface{}必须要使用var声明。

  • 使用方式:var name Typevar name = value;不能用 var name Type{}(这是局部变量的短声明风格)
  • []interface{}:Go 的”万能容器”,可存放任意类型,等价于[]any
  • 使用示例:
1
2
3
4
5
6
7
8
// 方案1:用 = 赋值(推荐!)
var define_1 = []any{}

// 方案2:先声明,后在 init/main 中初始化
var define_1 []any // 零值初始化为 nil
func init() {
define_1 = []any{} // 运行时初始化
}
  • 推荐使用方法 1,该方法不存在空指针!

[]any

  • []any是一个数据类型(不能单独作为值)

any 是 Go 1.18+ 中 interface{} 的别名(完全等价)

  • 花括号 {} 创建结构体实例

切片字面量

  • 切片字面量必须写成 []any{元素} 或 []any{}(空切片),即切片字面量必须包含 {}

使用示例:

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 intage = 0
浮点数(float64等) 0.0 var price float64price = 0.0
字符串(string) “” (空字符串) var name stringname = ""
布尔值(bool) false var isActive boolisActive = false
指针、切片、映射、通道、函数、接口 nil var ptr *intptr = nil
结构体 所有字段都是零值 var param dto.CreateProjectRequest → 所有字段为零值

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 - 临时编译并运行

1
go run main.go

🔄 临时编译:在临时目录编译代码
🚀 立即运行:编译完成后立即执行
🗑️ 自动清理:运行结束后自动删除临时文件
💡 开发友好:适合开发阶段快速测试

命名返回值

  • 在函数返回值中确定变量名:(rsp any, err error)

在 Go 中,你可以给返回值命名,而不只是声明类型。这样做的效果是:

  1. 自动声明变量rsperr 在函数体内被自动声明为局部变量。
  2. 零值初始化:它们会被初始化为各自类型的零值。
    • 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) {
// panic恢复
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
// 写法2:普通返回值(需要手动声明)
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  // ← 声明这个文件属于 tool 包

import "github.com/google/uuid"

// 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) {
//pattern := "^[a-zA-Z0-9:_-]{1,48}$"
str := "my-robot-01"
//isValid := ValidateRegex(pattern, str)
//fmt.Println(isValid)
if !ValidateRegex("^[a-zA-Z0-9:_-]{1,48}$", str) {
fmt.Println("invalid")
}
}

使用方法:

1
2
cd ./logic/tool
go test -v -run TestValidateRegex

完整代码

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"
)

// ✅ 正确:全局变量零值初始化(nil 切片)
var zes []any

// ✅ 正确:全局变量显式初始化(推荐!)
var initializedGlobal = []any{"global_item"}

func main() {
// 1. 处理 nil 切片(zes 是 nil)
fmt.Println("zes (nil 切片):", zes == nil) // true
zes = []any{}
// ✅ 安全:Go 允许对 nil 切片 append(但有风险!)
fmt.Println("append 到 nil 切片:", append(zes, 1)) // [1]

// 2. 局部变量初始化
a := []any{1, 2, 3} // ✅ 带元素
b := []any{} // ✅ 空切片
fmt.Println("b (空切片):", b) // []

// 3. interface{} 等价于 any
c := []interface{}{1, 2, 3}
m := []interface{}{}

// 4. 修复格式化字符串错误:
// %v 后不能跟逗号,必须用 Printf 系列
fmt.Printf("a 的值: %v, 类型: %T\n", a, a) // a 的值: [1 2 3], 类型: []interface {}
fmt.Printf("c 的值: %v\n", c) // c 的值: [1 2 3]
fmt.Printf("m 长度: %d\n", len(m)) // m 长度: 0
fmt.Printf("m 类型: %T\n", m) // m 类型: []interface {}
}