Go语言变量声明与初始化详解

变量声明与初始化

var关键字声明

Go语言使用var关键字声明变量,语法格式为:

go 复制代码
var 变量名 类型 = 表达式

其中类型和表达式可以省略一个:

go 复制代码
// 完整声明方式
var name string = "Alice"  // 显式指定类型为string
var count int = 100        // 显式指定类型为int
var isActive bool = true   // 显式指定类型为bool

// 省略类型,由编译器推断
var age = 25               // 编译器推断为int类型
var price = 99.99          // 编译器推断为float64类型
var greeting = "Hello"     // 编译器推断为string类型

// 省略表达式,初始化为零值
var score float64          // 初始化为0.0
var username string        // 初始化为空字符串""
var isReady bool           // 初始化为false

短变量声明(:=)

短变量声明是Go的特色语法,只能在函数内部使用:

go 复制代码
func main() {
    // 基本短变量声明
    name := "Alice"      // 等价于 var name string = "Alice"
    age := 25            // 等价于 var age int = 25
    ratio := 3.14        // 等价于 var ratio float64 = 3.14
    
    // 多变量短声明
    x, y := 10, "hello"  // 同时声明int和string变量
    
    // 在if、for等语句中的短声明
    if count := getCount(); count > 0 {
        fmt.Println(count)
    }
}

这种声明方式的特点:

  1. 必须包含初始值:不能只声明不赋值

    go 复制代码
    var x int      // 合法
    x :=           // 非法,必须提供初始值
  2. 不能指定类型:类型由编译器根据初始值推断

    go 复制代码
    x := int(10)   // 虽然可以这样写,但不推荐
  3. 简洁高效:是Go推荐的方式,特别是在函数内部

    go 复制代码
    // 推荐
    count := 0
    
    // 不推荐
    var count int = 0
  4. 作用域限制:只能在函数内部使用,不能在包级别使用

类型推断

Go语言的类型推断能力很强,例如:

go 复制代码
// 基本类型推断
var score = 98.5       // 推断为float64
var name = "Bob"       // 推断为string
var enabled = true     // 推断为bool
var list = []int{1,2,3} // 推断为[]int

// 复合类型推断
var person = struct{
    Name string
    Age  int
}{
    Name: "Alice",
    Age:  30,
}

// 函数返回值推断
func getValue() interface{} {
    return 42
}
var value = getValue()  // 推断为interface{}

特殊情况下需要显式指定类型:

go 复制代码
// 需要特定精度
var temperature float32 = 36.5  // 明确需要float32而非默认的float64

// 需要特定整数类型
var port uint16 = 8080          // 网络端口通常使用uint16

// 实现特定接口
var writer io.Writer = os.Stdout // 明确接口类型

零值(Zero Value)

Go为未初始化的变量提供默认零值:

类型 零值
数值类型 0
布尔类型 false
字符串 ""
指针 nil
接口 nil
切片 nil
映射 nil
通道 nil
函数 nil
结构体 各字段零值

零值在实际开发中的典型应用:

go 复制代码
// 计数器初始化
var requestCount int  // 自动初始化为0
requestCount++

// 延迟初始化
var logs []string     // 初始为nil,可以后续判断
if logs == nil {
    logs = make([]string, 0)
}

// 配置检查
var config *Config    // 初始为nil
if config != nil {
    config.Load()
}

// 结构体初始化
type User struct {
    Name string
    Age  int
}
var u User  // u.Name为"",u.Age为0

变量命名规则与最佳实践

命名规范

可见性规则
  1. 导出(公开)变量:首字母大写,包外可见

    go 复制代码
    var MaxRetries = 3      // 其他包可以访问
    var DefaultTimeout = 30 // 其他包可以访问
  2. 非导出(私有)变量:首字母小写,仅包内可见

    go 复制代码
    var maxRetries = 3      // 只在当前包内可用
    var defaultTimeout = 30 // 只在当前包内可用
命名风格
  1. 驼峰命名法

    go 复制代码
    var userName string     // 小驼峰
    var DatabaseConfig struct{} // 大驼峰
  2. 避免下划线(特殊常量除外)

    go 复制代码
    // 不推荐
    var user_name string
    var MAX_RETRIES = 3
    
    // 推荐
    var userName string
    var maxRetries = 3
  3. 避免使用关键字

    go 复制代码
    // 错误
    var var = "value"
    var func = "function"
    
    // 正确
    var variable = "value"
    var function = "func"
长度控制
  1. 短名称:适合局部变量和短作用域

    go 复制代码
    for i := 0; i < 10; i++ {
        sum += i
    }
    
    func process(data []byte) {
        buf := bytes.NewBuffer(data)
        // ...
    }
  2. 长名称:适合重要变量和全局变量

    go 复制代码
    var databaseConnectionPoolSize = 100
    var maximumAllowedFileSizeInBytes = 1024 * 1024 * 10

最佳实践示例

好的命名
go 复制代码
// 清晰的意图
var maxRetries = 3             // 明确表示最大重试次数
var connectionTimeout = 30     // 明确表示连接超时时间(秒)
var dbConfig DatabaseConfig    // 类型明确,知道是数据库配置

// 循环变量
for index, item := range items {
    processItem(index, item)
}

// 布尔变量
var isEnabled bool            // 使用is前缀表示布尔值
var hasPermission = false     // 使用has前缀表示布尔值
不好的命名
go 复制代码
// 缩写不明确
var mrt = 3                   // 不清楚mrt代表什么
var ct = 30                   // 不清楚ct代表什么

// 风格不一致
var timeout_of_connection = 30 // 混合使用下划线和驼峰
var ConnectionTimeout = 30     // 全局变量却使用小写

// 类型过于宽泛
var config interface{}        // 不知道具体是什么配置
var data []byte               // 不知道数据的用途

// 无意义的单字母
var d Data                    // 不知道d代表什么
var s string                  // 不知道s代表什么

变量作用域与生命周期

作用域规则

局部变量
go 复制代码
func calculate() {
    // 函数级作用域
    x := 10                // 在整个calculate函数内可见
    
    if y := 20; y > x {
        // 块级作用域
        z := 30            // 仅在if块内可见
        fmt.Println(x, y, z)
    }
    
    // fmt.Println(z)      // 错误:z未定义
    // fmt.Println(y)      // 错误:y未定义
    fmt.Println(x)         // 正确
}

func anotherFunc() {
    // fmt.Println(x)      // 错误:x未定义
}
全局变量
go 复制代码
package main

var globalCount = 0        // 包内所有函数可见

func increment() {
    globalCount++          // 可以访问和修改全局变量
}

func printCount() {
    fmt.Println(globalCount) // 可以访问全局变量
}

func main() {
    increment()
    printCount()           // 输出: 1
}
包级变量
go 复制代码
// config/config.go
package config

// 导出变量,其他包可访问
var AppName = "MyApp"     // 其他包可以通过config.AppName访问

// 非导出变量,仅本包可用
var apiKey = "secret"     // 只能在config包内使用

func GetAPIKey() string {
    return apiKey         // 包内函数可以访问非导出变量
}

生命周期管理

栈分配
go 复制代码
func sum(a, b int) int {
    // 简单变量通常分配在栈上
    result := a + b
    
    // 函数返回后,栈上的变量自动释放
    return result
}

func main() {
    s := sum(3, 4)  // 调用时在栈上分配空间
    fmt.Println(s)   // 使用完后栈空间自动回收
}
堆分配(逃逸分析)
go 复制代码
// User 结构体定义
type User struct {
    Name string
    Age  int
}

// 返回指针,导致变量逃逸到堆
func createUser() *User {
    u := User{Name: "Alice", Age: 30}  // 编译器会让u逃逸到堆
    return &u                          // 因为返回指针,所以必须在堆上分配
}

func main() {
    user := createUser()  // user指向堆上的User对象
    fmt.Println(user)     // 堆上对象由GC管理
}
查看逃逸分析结果

使用以下命令查看变量的逃逸分析情况:

bash 复制代码
go build -gcflags="-m -l" main.go

示例输出:

复制代码
./main.go:10:6: can inline createUser
./main.go:17:6: can inline main
./main.go:18:16: inlining call to createUser
./main.go:11:2: moved to heap: u  # 表示u变量逃逸到了堆

基本数据类型与变量

基本数据类型详解

整型
go 复制代码
// 平台相关整数
var a int = 10         // 32位系统上是int32,64位系统上是int64
var b uint = 20        // 无符号整数

// 明确大小的整数
var c int32 = 30       // 32位有符号整数
var d int64 = 40       // 64位有符号整数
var e uint8 = 50       // 8位无符号整数(0-255)

// 特殊别名
var f byte = 'A'       // uint8的别名,用于ASCII字符
var g rune = '中'      // int32的别名,用于Unicode字符

// 不同进制表示
var h = 0xFF           // 十六进制
var i = 0755           // 八进制
var j = 0b1101         // 二进制
浮点型
go 复制代码
// 标准浮点数
var f1 float32 = 3.1415926    // 单精度,约6-7位小数精度
var f2 float64 = 3.141592653589793 // 双精度,约15位小数精度(默认)

// 科学计数法
var avogadro = 6.02214076e23  // 阿伏伽德罗常数
var planck = 6.62607015e-34   // 普朗克常数

// 特殊浮点值
var posInf = math.Inf(1)      // 正无穷
var negInf = math.Inf(-1)     // 负无穷
var nan = math.NaN()          // 非数
布尔型
go 复制代码
var isReady bool = true
var hasError = false

// 布尔运算
a := true
b := false
fmt.Println(a && b)  // false
fmt.Println(a || b)  // true
fmt.Println(!a)     // false

// 条件判断
if isReady && !hasError {
    proceed()
}
字符串
go 复制代码
// 基本字符串
var name string = "张三"
var greeting = "Hello, 世界"

// 原始字符串(反引号)
var query = `SELECT * FROM users 
WHERE name = "Alice"`  // 保留换行和引号

// 字符串操作
s1 := "Hello"
s2 := "World"
combined := s1 + " " + s2  // 字符串连接
length := len(combined)    // 获取字节长度(不是字符数)

// 字符串索引
firstByte := combined[0]   // 获取第一个字节(H)
// combined[0] = 'h'       // 错误:字符串不可变

// Unicode处理
runeCount := utf8.RuneCountInString(combined) // 实际字符数

值类型 vs 引用类型

值类型示例
go 复制代码
// 基本类型都是值类型
a := 10
b := a       // 值拷贝
b = 20       // 修改b不影响a
fmt.Println(a, b)  // 输出: 10 20

// 数组是值类型
arr1 := [3]int{1, 2, 3}
arr2 := arr1        // 整个数组拷贝
arr2[0] = 100
fmt.Println(arr1)  // [1 2 3]
fmt.Println(arr2)  // [100 2 3]

// 结构体是值类型
type Point struct {
    X, Y int
}
p1 := Point{1, 2}
p2 := p1            // 结构体拷贝
p2.X = 10
fmt.Println(p1)     // {1 2}
fmt.Println(p2)     // {10 2}
引用类型示例
go 复制代码
// 切片是引用类型
slice1 := []int{1, 2, 3}
slice2 := slice1       // 引用同一个底层数组
slice2[0] = 100
fmt.Println(slice1)    // [100 2 3]
fmt.Println(slice2)    // [100 2 3]

// 映射是引用类型
map1 := map[string]int{"a": 1, "b": 2}
map2 := map1           // 引用同一个map
map2["a"] = 100
fmt.Println(map1)      // map[a:100 b:2]
fmt.Println(map2)      // map[a:100 b:2]

// 通道是引用类型
ch1 := make(chan int, 1)
ch2 := ch1             // 引用同一个通道
ch1 <- 42
fmt.Println(<-ch2)     // 42
指针操作
go 复制代码
// 基本指针操作
x := 10
p := &x        // 获取x的地址
fmt.Println(*p) // 10 (解引用)
*p = 20        // 通过指针修改x的值
fmt.Println(x)  // 20

// 结构体指针
type User struct {
    Name string
    Age  int
}
u := User{"Alice", 30}
up := &u
up.Age = 31     // 等同于 (*up).Age = 31
fmt.Println(u)  // {Alice 31}

// 指针作为函数参数
func increment(p *int) {
    *p++
}

num := 5
increment(&num)
fmt.Println(num)  // 6

// 返回局部变量指针(通过逃逸分析)
func newInt() *int {
    x := 42
    return &x    // 合法,x会被分配在堆上
}

多变量声明与特殊变量

多变量声明技巧

平行声明
go 复制代码
// 声明多个同类型变量
var a, b, c int
var x, y, z = 1, 2, 3

// 声明不同类型变量
var (
    name   = "Alice"
    age    = 30
    height = 1.65
)

// 函数中多变量短声明
func process() {
    a, b := 10, "hello"
    c, d := 3.14, true
    fmt.Println(a, b, c, d)
}
变量交换
go 复制代码
// 传统交换方式
a := 1
b := 2
temp := a
a = b
b = temp
fmt.Println(a, b)  // 2 1

// Go特有交换方式
x := 10
y := 20
x, y = y, x  // 直接交换
fmt.Println(x, y)  // 20 10

// 多值交换
i, j, k := 1, 2, 3
i, j, k = k, j, i  // i=3, j=2, k=1
分组声明
go 复制代码
// 包级变量分组
var (
    count   int
    total   float64
    name    string
    enabled bool
)

// 常量分组
const (
    Monday = iota + 1
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    Sunday
)

// 函数内变量分组
func process() {
    var (
        startTime time.Time
        duration  time.Duration
        err       error
    )
    // ...
}

空白标识符应用

忽略返回值
go 复制代码
// 忽略文件操作中的错误
file, _ := os.Open("example.txt")
defer file.Close()

// 忽略映射查找的第二个返回值
value := map[string]int{"a": 1}
v, _ := value["a"]   // 如果键存在,v=1
v, _ = value["b"]    // 如果键不存在,v=0

// 忽略函数返回值
_, err := someFunction()
if err != nil {
    handleError(err)
}
忽略循环索引
go 复制代码
// 忽略切片索引
names := []string{"Alice", "Bob", "Charlie"}
for _, name := range names {
    fmt.Println(name)
}

// 忽略映射键
scores := map[string]int{"Alice": 90, "Bob": 85}
for _, score := range scores {
    fmt.Println(score)
}

// 忽略通道索引
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for v := range ch {
    fmt.Println(v)
}
占位使用
go 复制代码
// 类型检查
var _ io.Reader = (*MyReader)(nil)  // 确保MyReader实现了io.Reader

// 导入包的副作用
import (
    _ "image/png"  // 注册PNG解码器
    "image/jpeg"   // 显式导入JPEG解码器
)

// 结构体字段占位
type Config struct {
    _    [0]int    // 填充对齐
    Port int
    _    struct{}  // 防止未keyed字面量
}

常量与枚举

常量定义

基本用法
go 复制代码
// 单行常量定义
const Pi = 3.141592653589793
const MaxRetry = 3
const AppName = "MyApp"

// 类型化常量
const StatusOk int = 200
const Timeout time.Duration = 30 * time.Second

// 常量表达式
const BufferSize = 1024 * 1024  // 1MB
const MaxUint = ^uint(0)        // 当前平台uint的最大值
const MinInt = -1 << 63        // 64位系统上int的最小值
批量声明
go 复制代码
// 简单批量声明
const (
    Success = 0
    Failure = 1
    Unknown = 2
)

// 使用iota的批量声明
const (
    Read   = 1 << iota // 1 << 0 = 1
    Write              // 1 << 1 = 2
    Execute            // 1 << 2 = 4
)

// 带表达式的批量声明
const (
    KB = 1024
    MB = KB * 1024
    GB = MB * 1024
    TB = GB * 1024
)

iota枚举实践

标准枚举
go 复制代码
// 定义枚举类型
type Weekday int

// 使用iota定义枚举值
const (
    Sunday Weekday = iota  // 0
    Monday                 // 1
    Tuesday                // 2
    Wednesday              // 3
    Thursday               // 4
    Friday                 // 5
    Saturday               // 6
)

// 使用枚举
today := Tuesday
fmt.Println(today)  // 输出: 2

// 为枚举类型添加方法
func (d Weekday) String() string {
    return [...]string{"Sunday", "Monday", "Tuesday", 
        "Wednesday", "Thursday", "Friday", "Saturday"}[d]
}

fmt.Println(today.String())  // 输出: Tuesday
位掩码枚举
go 复制代码
// 定义权限标志
const (
    FlagNone = 0
    FlagRead = 1 << iota   // 1
    FlagWrite              // 2
    FlagExecute            // 4
    FlagDelete             // 8
    FlagAll = FlagRead | FlagWrite | FlagExecute | FlagDelete // 15
)

// 使用位掩码
var permissions = FlagRead | FlagWrite  // 3

// 检查权限
if permissions&FlagRead != 0 {
    fmt.Println("Has read permission")
}

// 添加权限
permissions |= FlagExecute

// 移除权限
permissions &^= FlagWrite
表达式枚举
go 复制代码
// 基于iota的表达式枚举
const (
    _ = iota  // 忽略第一个值(0)
    KB = 1 << (10 * iota)  // 1 << (10*1) = 1024
    MB                     // 1 << (10*2) = 1048576
    GB                     // 1 << (10*3) = 1073741824
    TB                     // 1 << (10*4) = 1099511627776
)

// 带偏移的iota
const (
    Low = 5 + iota  // 5
    Medium          // 6
    High            // 7
    Critical        // 8
)

// 跳过某些值
const (
    A = iota  // 0
    B         // 1
    _         // 跳过2
    C         // 3
    D         // 4
)

变量与内存管理

内存优化技巧

基本类型优先
go 复制代码
// 使用基本类型而非引用类型
// 更好:栈分配
var coords [2]float64  // 数组,值类型
coords[0] = 10.5
coords[1] = 20.3

// 较差:堆分配
var coords []float64 = make([]float64, 2)  // 切片,引用类型
coords[0] = 10.5
coords[1] = 20.3

// 使用固定大小数组
var buffer [256]byte  // 栈上分配
// 优于
var buffer []byte = make([]byte, 256)  // 堆上分配
对象复用
go 复制代码
// 使用sync.Pool复用对象
var bufPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func process() {
    // 从池中获取Buffer
    buf := bufPool.Get().(*bytes.Buffer)
    defer bufPool.Put(buf)  // 使用后放回池中
    
    buf.Reset()  // 重置Buffer
    buf.WriteString("hello")
    fmt.Println(buf.String())
}

// 使用局部变量缓存
func expensiveCalculation() {
    var cache map[string]int
    if cache == nil {
        cache = make(map[string]int)
        // 初始化缓存
    }
    // 使用缓存...
}
大对象处理
go 复制代码
// 直接分配大对象(可能逃逸到堆)
var largeBuffer = make([]byte, 1<<20)  // 1MB

// 分块处理更优
const chunkSize = 32 * 1024  // 32KB
var chunks [][]byte

// 流式处理大文件
func processLargeFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    buf := make([]byte, 32*1024)  // 32KB缓冲区
    for {
        n, err := file.Read(buf)
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }
        processChunk(buf[:n])
    }
    return nil
}

性能分析工具

内存分析
bash 复制代码
# 实时内存分析
go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap

# 生成内存profile
go test -memprofile=mem.prof -bench=.

# 分析内存profile
go tool pprof -top -alloc_objects mem.prof
逃逸分析
bash 复制代码
# 查看逃逸分析结果
go build -gcflags="-m -l" main.go

# 示例输出
./main.go:10:6: can inline foo
./main.go:15:6: can inline bar
./main.go:20:6: can inline main
./main.go:21:11: inlining call to foo
./main.go:22:11: inlining call to bar
./main.go:11:10: []int literal escapes to heap
./main.go:16:10: []int literal does not escape
基准测试
go 复制代码
func BenchmarkStringConcatenation(b *testing.B) {
    var result string
    for i := 0; i < b.N; i++ {
        // 测试不同字符串拼接方式
        result = "Hello" + " " + "World"
    }
    _ = result
}

func BenchmarkStringBuilder(b *testing.B) {
    var result string
    for i := 0; i < b.N; i++ {
        var builder strings.Builder
        builder.WriteString("Hello")
        builder.WriteString(" ")
        builder.WriteString("World")
        result = builder.String()
    }
    _ = result
}

// 运行基准测试
// go test -bench=. -benchmem

常见问题与陷阱

典型错误案例

短声明误用
go 复制代码
func main() {
    // 错误示例1:重复声明
    var x int
    // x := 10  // 编译错误:no new variables on left side
    
    // 正确做法:至少声明一个新变量
    x, y := 10, 20  // 正确:y是新变量
    
    // 错误示例2:短声明在包级别
    // z := 30  // 错误:不能在包级别使用短声明
    
    // 特殊情况下重新声明
    file, err := os.Open("file1.txt")
    // ...处理file1...
    
    // 需要重新使用err变量
    file, err := os.Open("file2.txt")  // 正确:file是新变量
    // ...处理file2...
}
作用域混淆
go 复制代码
func printNumbers() {
    n := 1  // 函数级变量
    
    if true {
        n := 2  // 新建块级变量,遮蔽了外部的n
        fmt.Println(n)  // 2
    }
    
    fmt.Println(n)  // 1
    
    // 正确的做法:如果要修改外部n
    if true {
        n = 2  // 修改外部n
        fmt.Println(n)  // 2
    }
    fmt.Println(n)  // 2
}

// 另一个常见问题:循环变量
func loopIssue() {
    var funcs []func()
    for i := 0; i < 3; i++ {
        // 错误:所有闭包共享同一个i
        funcs = append(funcs, func() {
            fmt.Println(i)  // 全部输出3
        })
    }
    
    // 正确做法:创建局部副本
    for i := 0; i < 3; i++ {
        i := i  // 创建局部副本
        funcs = append(funcs, func() {
            fmt.Println(i)  // 输出0,1,2
        })
    }
}
零值陷阱
go 复制代码
// 映射未初始化
var m map[string]int
// m["key"] = 1  // 运行时panic: assignment to nil map

// 正确做法
m = make(map[string]int)
m["key"] = 1

// 切片未初始化
var s []int
// s[0] = 1  // 运行时panic: index out of range

// 正确做法
s = make([]int, 1)
s[0] = 1

// 接口未初始化
var w io.Writer
// w.Write([]byte("hello"))  // 运行时panic: nil pointer dereference

// 正确做法
w = os.Stdout
w.Write([]byte("hello"))

调试建议

使用vet工具
bash 复制代码
# 检查常见错误
go vet ./...

# 检查特定问题
go vet -copylocks ./...
go vet -printf ./...

# 集成到CI流程
# 在.gitlab-ci.yml或Jenkinsfile中添加
test:
  stage: test
  script:
    - go vet ./...
    - go test ./...
静态分析
bash 复制代码
# 使用staticcheck进行更深入的静态分析
staticcheck ./...

# 检查特定问题
staticcheck -checks "SA*" ./...  # 检查标准建议
staticcheck -checks "ST*" ./...  # 检查风格问题

# 集成到开发流程
# 在pre-commit钩子中添加
#!/bin/sh
staticcheck ./...
if [ $? -ne 0 ]; then
    echo "Static analysis failed"
    exit 1
fi
调试技巧
go 复制代码
// 使用调试变量
var debug = os.Getenv("DEBUG") == "1"

func process(data []byte) {
    if debug {
        log.Printf("Processing %d bytes", len(data))
    }
    // ...
}

// 条件编译
// +build debug

package main

var debugMode = true

// 使用runtime/debug
import "runtime/debug"

func handlePanic() {
    if r := recover(); r != nil {
        debug.PrintStack()
        // 恢复处理...
    }
}

// 使用pprof实时分析
import _ "net/http/pprof"

func main() {
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()
    // 应用主逻辑...
}

最佳实践总结

声明风格

  1. 优先使用短声明(:=)

    go 复制代码
    // 函数内部优先使用短声明
    count := 0
    name := "Alice"
    
    // 仅在需要时才使用var
    var globalConfig Config  // 包级变量
  2. 全局变量使用var显式声明

    go 复制代码
    // 包级变量使用var
    var (
        maxConnections = 100
        timeout        = 30 * time.Second
    )
  3. 常量使用const声明

    go 复制代码
    // 常量使用const
    const (
        MaxRetries = 3
        DefaultPort = 8080
    )

命名规范

  1. 保持一致性(全项目统一风格)

    go 复制代码
    // 选择一种风格并保持一致
    var maxRetries = 3       // 小驼峰
    var ConnectionTimeout = 30 // 大驼峰导出的
    
    // 避免混合风格
    // 不要这样: var max_retries = 3
  2. 重要变量添加注释

    go 复制代码
    // timeoutMs specifies the maximum wait time in milliseconds
    // before aborting the request. Zero means no timeout.
    var timeoutMs = 5000
    
    // cache stores pre-computed results to improve performance.
    var cache = make(map[string]interface{})
  3. 避免无意义的名称

    go 复制代码
    // 不好
    var a int
    var b string
    var c []byte
    
    // 好
    var age int
    var name string
    var data []byte

作用域控制

  1. 最小作用域原则

    go 复制代码
    func process(data []byte) {
        // 仅在需要时声明变量
        if len(data) > 0 {
            first := data[0]
            // 使用first...
        }
        // first不可见,作用域受限
    }
  2. 避免不必要的全局变量

    go 复制代码
    // 不好: 不必要的全局状态
    var count int
    
    func increment() {
        count++
    }
    
    // 好: 封装状态
    type Counter struct {
        count int
    }
    
    func (c *Counter) Increment() {
        c.count++
    }
  3. 合理使用闭包

    go 复制代码
    func NewCounter() func() int {
        var count int
        return func() int {
            count++
            return count
        }
    }
    
    func main() {
        counter := NewCounter()
        fmt.Println(counter())  // 1
        fmt.Println(counter())  // 2
    }
相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔2 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说7 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json