前言

环境搭好(见上篇),开始正式修炼 Go 语法。Go 的设计哲学是 少即是多——关键字只有 25 个,没有继承、没有异常、没有三元运算符,但每个特性都精打细磨。

一、变量与常量

package main

import "fmt"

func main() {
	// 三种声明方式
	var a int = 10      // 完整声明
	var b = 3.14        // 类型推断
	c := "hello"        // 短声明(只能用在函数内, 最常用)

	// 多变量
	x, y := 1, 2
	x, y = y, x         // 交换变量, 不需要中间变量!

	// 常量(编译期确定, 不可修改)
	const Pi = 3.14159
	const (
		StatusOK  = 200
		StatusErr = 500
	)

	// iota: 自动递增的常量生成器
	const (
		Mon = iota + 1   // 1
		Tue              // 2
		Wed              // 3
	)

	fmt.Println(a, b, c, x, y, Pi, Mon, Tue, Wed)
}

⚠️ Go 没有"未使用警告"那么温柔——声明了不用的变量直接编译报错,逼你保持代码干净。

二、基本类型与零值

类别 类型 零值
整型 int, int8/16/32/64, uint... 0
浮点 float32, float64 0.0
布尔 bool false
字符串 string ""(空串, 不是 nil)
指针/切片/map/接口/函数/channel *T, []T, map[K]V… nil

Go 没有隐式类型转换,必须显式:

var i int = 42
var f float64 = float64(i)   // 必须显式转
var u uint = uint(f)

字符串常用操作:

s := "hello, 世界"
len(s)                        // 字节数: 13(UTF-8 下汉字 3 字节)
utf8.RuneCountInString(s)     // 字符数: 9
strings.Contains(s, "世界")    // true
strings.Split("a,b,c", ",")   // [a b c]
fmt.Sprintf("%s=%d", "n", 5)  // 格式化字符串

三、指针(比 C 简单)

func main() {
	x := 10
	p := &x         // p 是指向 x 的指针
	fmt.Println(*p) // 解引用: 10
	*p = 20
	fmt.Println(x)  // 20
}

Go 指针不能做算术运算(不能 p++),避免了 C 的一整类内存错误。

四、函数:多返回值是杀手锏

// 多返回值 + 命名返回值
func divide(a, b int) (result int, err error) {
	if b == 0 {
		err = fmt.Errorf("除数不能为零")
		return            // 裸返回
	}
	result = a / b
	return
}

// 可变参数
func sum(nums ...int) int {
	total := 0
	for _, n := range nums {
		total += n
	}
	return total
}

// 函数是一等公民: 可以赋值、传参
f := func(x int) int { return x * x }
fmt.Println(f(5))

调用侧的惯用法(后面错误处理那篇会展开):

r, err := divide(10, 0)
if err != nil {
	fmt.Println("出错了:", err)
}

五、控制流:只有 for

Go 把所有循环统一成了 for:

// 经典三段式
for i := 0; i < 5; i++ { ... }

// 相当于 while
n := 0
for n < 10 { n++ }

// 死循环
for { break }

// 遍历切片
nums := []int{1, 2, 3}
for i, v := range nums { fmt.Println(i, v) }

// 遍历 map
m := map[string]int{"a": 1}
for k, v := range m { fmt.Println(k, v) }

// 不需要下标时用 _ 丢弃
for _, v := range nums { ... }

if 的特殊语法——可以带初始化语句(限定了变量作用域):

if err := doSomething(); err != nil {
	fmt.Println("failed:", err)
}
// 这里已经访问不到 err 了

switch 更强大——默认 break,一个 case 可多值:

switch day := 3; day {
case 1, 2, 3, 4, 5:
	fmt.Println("工作日")
case 6, 7:
	fmt.Println("周末")
default:
	fmt.Println("非法")
}

// 无表达式 switch, 替代长串 if-else
switch {
case score >= 90:
	grade = "A"
case score >= 60:
	grade = "B"
default:
	grade = "C"
}

六、defer:延迟执行的清理利器

func readFile(path string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()        // 函数返回前必定执行, 不怕中间 return

	// ... 读文件逻辑
	return nil
}

要点:

  • 多个 defer 后进先出(栈):defer a(); defer b() → 执行顺序 b → a
  • defer 的参数在声明时求值:
i := 0
defer fmt.Println(i)   // 打印 0, 不是 1
i = 1
  • 典型用途:关文件、解锁 mu.Unlock()、recover 捕获 panic

小结

特性 一句话
:= 函数内短声明,最常用
零值 没初始化也不会是"随机垃圾值"
多返回值 (result, err) 惯例的根基
for 唯一的循环关键字
switch 默认 break,case 可多值
defer 资源清理标配,LIFO

语法过关后,下一站是复合类型:数组、切片与映射——Go 数据结构的主战场。


本文是「Golang」系列第 2 篇。