Go :如何声明变量(var)与常量(const)

7 阅读8分钟

序言

🐒:
   今天开始学习 Go 语言的 变量与常量,该文章会记录一些相关的知识点和案例。


变量

定义

用于存储程序运行时可改变的数据


声明方式


方式语法适用场景示例
标准声明var 变量名 类型 = 表达式显式指定类型 或 需要零值初始化var age int = 25
类型推断var 变量名 = 表达式省略类型,由右侧值推导var age = 25
声明赋值var 变量名 类型
变量名 = 表达式
省略类型,由右侧值推导先声明,后赋值var age int
age = 25
短变量声明变量名 := 表达式函数内部,最常用、最简洁age := 25
多变量声明var (变量1 类型1 = 值1 ...)
var 变量1,变量2 = 值1,值2 )
批量声明,常用于包级别var (age int = 25; name string = "windy")
var a, b = 1,2

示例:标准声明


package main

import "fmt"

func main() {
	var age int = 25
	fmt.Println(age)
}
25

🐒:
   这种声明方式虽然很清晰明了,但对我来说有点麻烦。代码敲多了的时候,是不会考虑这种,因为有点累脑费眼,所以优先于简洁的方式。


示例:类型推断


package main

import "fmt"

func main() {
	var age = 25
	fmt.Println(age)
}
25

🐒:
   这种声明方式相对于第一个来说简单了些,我一般用于全局变量(看情况进行初始化),例如存储验证码。


示例:声明赋值


package main

import "fmt"

func main() {
    var age int
    age = 25
    fmt.Println(age)
}
25

🐒:
   这种声明方式乍一看比第一个还复杂,但我用的比较多,例如初始化结构体,然后根据需求挨个进行赋值。


示例:短变量声明


package main

import "fmt"

func main() {
    age := 25
    fmt.Println(age)
}
25

🐒:
   这种声明方式最简洁,我用的也多,例如调用函数的时接收结果( res, _ := myFun() )。


示例:多变量声明


import "fmt"

func main() {
    var (
       age  int    = 25
       name string = "windy"
    )
    fmt.Println(age, name)
    
    var a, b = 1, 2
    fmt.Println(a, b)
}
// 也可以删除类型,结果是一样的
25 windy
12

🐒:
   这种声明方式我基本上就是了解了解,就没用过。


短变量声明的一些小细节

作用域限制: 只能用在函数内部,不能用于包级别
重复声明规则: 在同一代码块中,若左侧变量名至少有一个是新变量,则允许对旧变量使用 :=(此时旧变量的行为是赋值而非重新声明)。
特殊的空白标识符 : 如果函数的返回值有些不需要,可以给变量取名为下划线 _


示例:重复声明规则

运行成功

package main

import "fmt"

func myFunOne() (string, error) {
	return "Hello", nil
}

func myFunTwo() (string, error) {
	return "world", nil
}

func main() {
	one, err := myFunOne() // 声明 a 和 err
	two, err := myFunTwo() // 声明 b,对 err 赋值(合法,因为 b 是新的)
	fmt.Println(one, two, err)
}

Hello world <nil>

没编译时就提示报错

package main

import "fmt"

func myFunOne() (string, error) {
    return "Hello", nil
}

func myFunTwo() (string, error) {
    return "world", nil
}

func main() {
    one, err := myFunOne() // 声明 a 和 err
    one, err := myFunTwo()  // 报错,左侧无新变量
    fmt.Println(one, err)
}
// 报错提示:No new variables on the left side of ':=', Replace with'='
// 左边没有新变量,直接用 = 即可(会覆盖之前的值)

特殊的空白标识符

Go 的短变量声明 := 中,下划线 _ 是一个特殊的空白标识符。它用于占位,表示主动忽略某个返回值或声明项。关于它的使用,有几个关键点需要特别注意:

  • _ 不算“新变量”,不参与重复声明规则的判定

    • 在 := 左侧,只要出现至少一个非 _ 的新变量名,声明就是合法的。
    • _ 可以被重复使用任意多次,它不会导致编译错误,也不会被视为新的有效变量。
  • _ 无法被读取,只用于占位丢弃值

    • _ 本质上不绑定任何值。你不能试图读取或使用它。
    • 它最常见的场景是处理函数的多返回值,比如忽略 error
  •  _ 声明的变量 “未使用” 不会报错

    • Go 编译器强制要求声明的非 _ 变量必须被使用。但 _ 是唯一的例外,它可以被多次 “声明” 且永不使用。
  • _ 不能用于常量或类型声明

    • _ 仅在 变量声明 和 赋值 场景作为占位符,不能用于 const 或 type 定义。
  • 在 := 中左侧全是 _ 是语法错误

    • 即使你只是为了调用一个函数并忽略其所有返回值,也不能写 _ := myFun()
    • 正确做法是 直接调用使用普通赋值(非短声明)
  • 记住:把 _ 当成黑洞,它只为满足语法结构而存在,不参与任何变量计数的逻辑。


常量


定义

编译时确定、运行时不改变的值


声明方式


方式语法示例
标准声明const 常量名 类型 = 表达式const Age int = 25
类型推断const 常量名 = 表达式const Age = 25
多常量声明const (常量1 类型1 = 值1 ...)const (Age int = 25; Name string = "windy")
隐式重复多常量声明块中省略右值时自动沿用上一行表达式const ( A = 1; B; C )

示例:标准声明


package main

import "fmt"

func main() {
    const age int = 25
    fmt.Println(age)
}
25

🐒:
   这种声明方式虽然有点麻烦,但很清晰明了。一般来说,常量定义的数量比变量少很多,且大多数都定义在函数外,调用常量的时候能一下子知道是什么类型的会比较好。


示例:类型推断


package main

import "fmt"

func main() {
    const age = 25
    fmt.Println(age)
}
25

🐒:
   这种声明方式个人习惯上不常使用,推荐第一种。


示例:多常量声明


package main

import "fmt"

func main() {
    const (
       age  int    = 25
       name string = "windy"
    )
    fmt.Println(age, name)
}
// 也可以删除类型,结果是一样的
25 windy

🐒:
   这种声明方式一般在定义差不多含义的常量的时候使用,比如返回码( 200,404,500 等 )。


示例:隐式重复


package main

import "fmt"

func main() {
    const (
       Apple  = "fruit"
       Banana // = "fruit"
       Cherry // = "fruit"
    )
    // Banana 和 Cherry 的值都是 "fruit"
    fmt.Println(Apple, Banana, Cherry)
}
fruit fruit fruit

🐒:
   基本上没用过这种声明方式,不知道以后会不会有用得到的场景


命名规则


标识符语法规则(强制)

  • 由字母(Unicode 字母)、数字、下划线 _ 组成。
  • 不能以数字开头
  • 区分大小写,例如 Name 和 name 是不同的。
  • 不能是 Go 语言的 关键字,例如 funcvarconstiffor 等。
  • 下划线 _  是特殊的空白标识符,不能作为常规变量名使用(声明即丢弃)。

导出规则(可见性控制)

Go 没有 public/private 关键字,而是用 首字母的大小写 控制包外可见性:

首字母可见性术语
大写包外可访问Exported (导出标识符)
小写下划线仅包内可访问Unexported (未导出标识符)

命名风格约定(非强制,但强烈推荐)

Go 社区遵循一套统一的命名风格,保证了代码的可读性和一致性:

对象类型风格示例
变量驼峰式 (camelCase)userCounthttpRequest
常量驼峰式 (PascalCasecamelCase)MaxRetries (导出), defaultPort (未导出)
函数 / 方法驼峰式GetUserparseData
类型名驼峰式,首字母大写(导出)UserServicehttpClient (未导出)
包名全小写,简短,无下划线timehttputilstrings
接口名通常以 er 结尾(单方法接口)ReaderWriterCloser
缩写词全大写或全小写,保持一致HTTPServer (或 httpServer),不能写成 HttpServer

特殊命名约定 与 注意点

  • _ (空白标识符) :用于占位忽略值。
  • _test 后缀:文件名 xxx_test.go 是测试文件,包名通常是 xxx_test 或原包名。
  • 内部包 internal:路径中包含 /internal/ 的包只能被其父目录下的包导入,是一种强制的可见性控制。
  • 避免无意义的包名:如 utilcommonhelper,尽量使用描述功能的具体名字。

使用范围(作用域 Scope)


作用域决定了标识符( 变量名、常量名 )在代码中的可见范围。Go 的作用域是词法作用域静态作用域),由代码块 {} 界定。


作用域层级(从大到小)


级别声明位置可见范围
全局作用域所有函数之外整个包(package)内
包级私有函数外,首字母小写仅当前包
包级导出函数外,首字母大写当前包 + 其他导入此包的包
函数作用域函数体内任何位置该函数体内(从声明点到函数结束)
块作用域ifforswitch 的 {} 内仅该花括号块内

作用域遮蔽 (Shadowing)

内层作用域声明的变量若与外层同名,会 遮蔽 外层变量。这是 Go 代码中非常常见的


示例


package main

import "fmt"

var myName = "true" // 包级变量

func main() {
    fmt.Println(myName) // 输出 true

    // 在块内声明同名变量,创建了新变量而非修改外层
    if true {
       var myName = "false"
       fmt.Println(myName)
       // 这个 myName 是块内新变量,值为 false
    }
    fmt.Println(myName) // 仍然输出 true,包级变量未被修改
}
true
false
true

🐒:
   为了节省脑子,尽量不重名,如果实在不想思考其他切合的名字,就直接 name1name2


避免遮蔽的建议


  • 使用 go vet 的 -shadow 选项检测(go vet -shadow ./...)。
  • 在内部作用域中,若意图修改外层变量,直接使用赋值 = 而非 :=

包级变量 与 常量 的初始化顺序

  • 在同一个包内,多个 .go 文件中的包级变量按 文件名字母顺序 初始化。
  • 单个文件内,按声明顺序 从上到下 初始化。
  • 若初始化表达式引用其他包级变量,Go 会处理依赖关系,禁止循环引用
  • 常量在编译时求值,不依赖运行时初始化顺序。