第3章 Go 基础语法速览:变量与常量、数据类型、控制结构、函数与方法

好,咱们直接上手。国密 SDK 的适配代码说到底还是 Go 语言写的,你得先过了语法这一关才能跟 SM2、SM3 打交道。我带过不少新手,发现大家容易在变量声明、切片用法、以及闭包的坑里卡住。下面我把最核心的几个点串一遍,你跟着走就行。

3.1 变量与常量

Go 的变量声明有好几种方式,我个人最常用的是短声明 :=,省事。但要注意,:= 只能在函数内部用,包级别得用 var。

package main

import "fmt"

var version = "1.0.0"        // 包级变量

func main() {
    name := "国密SDK"          // 短声明
    var age int = 3            // 显示声明
    fmt.Println(name, age)
}
小技巧:常量用 const 定义,iota 可以用来生成枚举值。我在写算法标识常量时经常用 iota,比如 SM2=0, SM3=1,后续加新算法自动递增。

3.2 基本数据类型

类型分类具体类型说明
布尔bool只有 true 和 false
整数int, int8, int16, int32, int64带符号;int 在 64 位机上就是 64 位
无符号整数uint, uint8…uint64适合位运算和字节操作
浮点数float32, float64国密算法里基本用 int 和 []byte
字符串string不可变,UTF-8 编码
字节byte (即 uint8)处理二进制数据的主力
派生类型数组、切片、map、struct、接口稍后会细讲

讲个真实经验:当初适配 SM3 时,有人把 int 和 uint 混用做哈希长度计算,结果在 32 位和 64 位机器上行为不一致。记住:涉及密码学长度计算,尽量用 int 或明确大小的类型。

3.3 复杂数据类型:数组、切片与 map

数组 vs 切片

数组长度固定,切片更灵活。我几乎不用数组,全是切片。来看对比:

// 数组:长度固定
var arr [3]int = [3]int{1, 2, 3}

// 切片:可动态增长
s := []int{1, 2, 3}
s = append(s, 4)            // ok
曾经踩过的坑:切片共享底层数组!如果你把一个切片传给函数并修改了元素,外面也会变。想要独立副本,老老实实用 copy()。

map

map 就是键值对,声明时记得初始化,否则会是 nil,写入会 panic。

m := make(map[string]int)
m["SM2"] = 1
v, ok := m["SM4"]
if !ok {
    fmt.Println("key not found")
}

3.4 控制结构

Go 只有 for,没有 while。但你可以用 for 模拟一切。

// 标准 for
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

// 类似 while
n := 0
for n < 5 {
    n++
}

// 无限循环
for {
    // 常用在协程监听
}

if 和 switch 也很简洁。我最喜欢的是 switch 不需要 break,默认只命中一个分支。

alg := "SM2"
switch alg {
case "SM2":
    fmt.Println("椭圆曲线签名")
case "SM3":
    fmt.Println("哈希")
default:
    fmt.Println("未知算法")
}

3.5 函数与方法

函数是一等公民,可以有多个返回值。这在国密适配里很常用——函数返回结果和错误。

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为0")
    }
    return a / b, nil
}

方法就是带接收者的函数。想象成给类型附加行为。

type SM2Key struct {
    D []byte
}

// 方法:接收者是 SM2Key 的指针
func (k *SM2Key) Sign(data []byte) ([]byte, error) {
    // 签名逻辑…
    return nil, nil
}
我自己的习惯:如果方法需要修改接收者,用指针;如果只是读取,用值。但为了统一,我基本都写指针,避免意外复制。

3.6 本章知识结构

下面我用一张图把这几个部分串起来,方便你整体把握。

Go基础语法结构图 Go 基础语法知识图谱 Go 基础语法 变量与常量 数据类型 控制结构 函数与方法 var / := / const iota 枚举 基本类型 切片/map if / switch for 循环 多返回值 方法接收者 国密适配中,切片和 map 最常用;方法用来封装密钥对象的操作。

3.7 避坑总结

  • 变量声明:短声明 := 不能在包级别用,否则编译报错。
  • 切片共享:函数内修改切片元素,外面也会变。可以用 copy 产生独立数据。
  • map 未初始化:写 nil map 会 panic,用 make 创建。
  • switch 默认穿透:不用写 break,但如果你想继续执行下一个 case,需要显式 fallthrough。

实用提示:在国密 SDK 里,你会经常看到 func (k *Key) Encrypt(data []byte) ([]byte, error) 这种模式。多个返回值的设计让错误处理非常干净,不用像 C 一样检查返回码。