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
    }
相关推荐
He1955015 分钟前
Go初级二
开发语言·后端·golang
我是廖志伟22 分钟前
【jar包启动,每天生成一个日志文件】
java·jar
掉鱼的猫33 分钟前
Solon StateMachine 实现状态机使用示例详解
java·状态机
用户61354114601639 分钟前
BurpSuite 1.4.07.jar 怎么使用?详细安装和抓包教程(附安装包下载)
java
ankleless39 分钟前
Spring 框架深度解析:从核心原理到实战应用
java·spring
带刺的坐椅41 分钟前
Spring AOP 与 Solon AOP 有什么区别?
java·spring·solon·aop
华仔啊1 小时前
Java序列化详解:什么情况下必须用它?
java
草莓熊Lotso1 小时前
【C++】--函数参数传递:传值与传引用的深度解析
c语言·开发语言·c++·其他·算法
Ice__Cai1 小时前
Flask 路由详解:构建灵活的 URL 映射系统
开发语言·python·flask
BillKu2 小时前
Spring Boot Controller 使用 @RequestBody + @ModelAttribute 接收请求
java·spring boot·后端