02-Go基础语法

Go语法简洁,吸收C系语言优点,去除冗余特性。25个关键字,学习成本低。

数据类型

基本类型

// 整型
int8, int16, int32, int64
uint8, uint16, uint32, uint64
int                    // 32或64位(取决于平台)
uint                   // 无符号int
byte                   // uint8别名
rune                   // int32别名,表示Unicode码点

// 浮点型
float32, float64

// 复数
complex64, complex128

// 布尔型
bool                   // true或false

// 字符串
string                 // UTF-8编码,不可变

类型零值

Go变量声明后自动初始化为零值(无未定义行为)。

var i int        // 0
var f float64    // 0.0
var b bool       // false
var s string     // ""(空字符串)
var p *int       // nil
var slice []int  // nil
var m map[string]int  // nil
var ch chan int  // nil
var fn func()    // nil

类型转换

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

// 字符串转换
import "strconv"

i, err := strconv.Atoi("123")     // 字符串→int
s := strconv.Itoa(123)            // int→字符串
f, err := strconv.ParseFloat("3.14", 64)
s := strconv.FormatFloat(3.14, 'f', 2, 64)

变量声明

四种方式

// 1. var声明
var name string = "Alice"
var age int = 25

// 2. 类型推断
var name = "Alice"  // 自动推断为string

// 3. 短变量声明(函数内)
name := "Alice"     // 最常用
age := 25

// 4. 批量声明
var (
    name string = "Alice"
    age  int    = 25
    city string
)

常量

const Pi = 3.14159
const MaxSize = 100

// 批量常量
const (
    Red   = 0
    Green = 1
    Blue  = 2
)

// iota(自增)
const (
    _  = iota  // 0,跳过
    KB = 1 << (10 * iota)  // 1024
    MB                      // 1048576
    GB                      // 1073741824
)

const (
    Monday = iota + 1  // 1
    Tuesday            // 2
    Wednesday          // 3
)

基本语法

控制结构

// if(无需括号)
if x > 0 {
    // ...
} else if x < 0 {
    // ...
} else {
    // ...
}

// if with初始化
if err := doSomething(); err != nil {
    return err
}
// err作用域仅在if块内

// switch(无需break,自动break)
switch x {
case 1:
    // ...
case 2, 3:  // 多个条件
    // ...
default:
    // ...
}

// switch with初始化
switch err := doSomething(); err {
case nil:
    // ...
default:
    return err
}

// switch无表达式(替代if-else链)
switch {
case x > 0:
    // ...
case x < 0:
    // ...
default:
    // ...
}

// fallthrough(显式穿透)
switch x {
case 1:
    fmt.Println("one")
    fallthrough  // 继续执行case 2
case 2:
    fmt.Println("two")
}

// for循环(唯一循环语句)
for i := 0; i < 10; i++ {
    // ...
}

// while风格
for condition {
    // ...
}

// 无限循环
for {
    // ...
    break
}

// range遍历
for i, v := range slice {
    // i是索引,v是值
}

for k, v := range map {
    // k是键,v是值
}

for i := range slice {
    // 仅索引
}

for _, v := range slice {
    // 忽略索引(_是空标识符)
}

// 标签和goto(少用)
Loop:
for {
    for {
        break Loop  // 跳出外层循环
    }
}

函数

函数定义

// 基本函数
func add(a int, b int) int {
    return a + b
}

// 简化参数
func add(a, b int) int {
    return a + b
}

// 多返回值
func swap(a, b int) (int, int) {
    return b, a
}

x, y := swap(1, 2)

// 命名返回值
func divide(a, b int) (result int, err error) {
    if b == 0 {
        err = errors.New("division by zero")
        return  // 返回result和err的当前值
    }
    result = a / b
    return
}

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

sum(1, 2, 3, 4, 5)
nums := []int{1, 2, 3}
sum(nums...)  // 展开切片

函数类型和闭包

// 函数类型
type Operation func(int, int) int

func apply(op Operation, a, b int) int {
    return op(a, b)
}

apply(func(a, b int) int { return a + b }, 3, 4)

// 闭包
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

c := counter()
c()  // 1
c()  // 2

defer延迟执行

// defer按LIFO顺序执行
func example() {
    defer fmt.Println("3")
    defer fmt.Println("2")
    fmt.Println("1")
}
// 输出:1 2 3

// 常用于资源清理
func readFile() error {
    f, err := os.Open("file.txt")
    if err != nil {
        return err
    }
    defer f.Close()  // 函数返回前自动关闭
    
    // 读取文件
    return nil
}

// defer参数立即求值
func deferTest() {
    x := 1
    defer fmt.Println(x)  // 立即求值:1
    x = 2
    // 输出:1(不是2)
}

数据结构

数组

固定长度,值类型。

// 声明
var arr [5]int              // [0, 0, 0, 0, 0]
arr := [5]int{1, 2, 3, 4, 5}
arr := [...]int{1, 2, 3}    // 自动计算长度

// 访问
arr[0] = 10
len(arr)  // 5

// 数组是值类型
arr2 := arr  // 复制整个数组

切片(slice)

动态数组,引用类型,最常用。

// 创建
var s []int                  // nil切片
s := []int{1, 2, 3}
s := make([]int, 5)          // 长度5,容量5
s := make([]int, 5, 10)      // 长度5,容量10

// 切片数组
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4]                // [2, 3, 4]
s := arr[:3]                 // [1, 2, 3]
s := arr[2:]                 // [3, 4, 5]

// 操作
s = append(s, 6)             // 追加元素
s = append(s, 7, 8, 9)       // 追加多个
s = append(s1, s2...)        // 合并切片
copy(dst, src)               // 复制
len(s)                       // 长度
cap(s)                       // 容量

// 删除元素
s = append(s[:i], s[i+1:]...)  // 删除索引i
s = s[:len(s)-1]               // 删除最后一个
s = s[1:]                      // 删除第一个

映射(map)

哈希表,引用类型。

// 创建
var m map[string]int         // nil map(不能写入)
m := map[string]int{}        // 空map
m := make(map[string]int)
m := map[string]int{"a": 1, "b": 2}

// 操作
m["key"] = value             // 添加/更新
value := m["key"]            // 读取
value, ok := m["key"]        // 检查存在性
if ok {
    // key存在
}

delete(m, "key")             // 删除
len(m)                       // 元素个数

// 遍历(无序)
for k, v := range m {
    fmt.Println(k, v)
}

for k := range m {  // 仅键
    // ...
}

结构体(struct)

// 定义
type Person struct {
    Name string
    Age  int
    City string
}

// 创建
p1 := Person{"Alice", 25, "Beijing"}
p2 := Person{Name: "Bob", Age: 30}  // 字段名初始化
p3 := Person{Name: "Charlie"}       // 未指定字段为零值
var p4 Person                       // 零值初始化

// 访问
p1.Name = "Alice2"
age := p1.Age

// 结构体指针
p := &Person{Name: "Alice"}
p.Age = 25  // 自动解引用(无需p->Age)

// 匿名字段(嵌入)
type Student struct {
    Person        // 匿名字段,继承Person的字段
    StudentID string
}

s := Student{Person: Person{Name: "Alice"}, StudentID: "123"}
s.Name  // 直接访问Person.Name

// 标签(tag)
type User struct {
    Name  string `json:"name" db:"user_name"`
    Email string `json:"email" validate:"required,email"`
}

指针

Go有指针但无指针运算,更安全。

// 指针声明
var p *int
p = &x  // 取地址

// 解引用
*p = 10

// new分配内存
p := new(int)  // 返回*int,值为0

// 指针vs值
func modifyValue(x int) {
    x = 100  // 不影响原变量
}

func modifyPointer(p *int) {
    *p = 100  // 修改原变量
}

x := 1
modifyValue(x)
fmt.Println(x)  // 1

modifyPointer(&x)
fmt.Println(x)  // 100

方法

Go无类,用方法为类型添加行为。

type Rectangle struct {
    Width, Height float64
}

// 值接收者
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 指针接收者(可修改)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

r := Rectangle{Width: 10, Height: 5}
r.Area()    // 50
r.Scale(2)  // Width=20, Height=10

// 任意类型都可定义方法
type MyInt int

func (m MyInt) IsEven() bool {
    return m%2 == 0
}

var x MyInt = 10
x.IsEven()  // true

选择: 需要修改或大对象用指针接收者,否则用值接收者。

接口

隐式实现,鸭子类型。

// 接口定义
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// 组合接口
type ReadWriter interface {
    Reader
    Writer
}

// 实现接口(无需显式声明)
type File struct {}

func (f *File) Read(p []byte) (int, error) {
    // 实现Read,File就实现了Reader接口
    return 0, nil
}

// 空接口(任意类型)
interface{}  // 或 any(Go 1.18+)

func Print(v interface{}) {
    fmt.Println(v)
}

Print(42)
Print("hello")
Print([]int{1, 2, 3})

// 类型断言
var i interface{} = "hello"
s := i.(string)              // "hello"
s, ok := i.(string)          // "hello", true(安全断言)
n, ok := i.(int)             // 0, false

// 类型switch
switch v := i.(type) {
case int:
    fmt.Printf("int: %d\n", v)
case string:
    fmt.Printf("string: %s\n", v)
default:
    fmt.Printf("unknown type\n")
}

错误处理

Go使用多返回值处理错误,无异常机制。

import "errors"

// 返回错误
func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// 使用
result, err := divide(10, 0)
if err != nil {
    log.Fatal(err)  // 处理错误
}

// 自定义错误
type MyError struct {
    Code    int
    Message string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("code %d: %s", e.Code, e.Message)
}

// errors.Is(Go 1.13+)
var ErrNotFound = errors.New("not found")

if errors.Is(err, ErrNotFound) {
    // 错误匹配
}

// errors.As(类型转换)
var myErr *MyError
if errors.As(err, &myErr) {
    fmt.Println(myErr.Code)
}

// 错误包装
return fmt.Errorf("failed to process: %w", err)

并发基础

goroutine

轻量级线程,栈初始2KB,可创建百万级。

// 启动goroutine
go func() {
    fmt.Println("goroutine")
}()

go processData()

// 匿名函数闭包
for i := 0; i < 5; i++ {
    go func(n int) {  // 参数传递,避免闭包陷阱
        fmt.Println(n)
    }(i)
}

// 等待goroutine(用sync.WaitGroup)
import "sync"

var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(n int) {
        defer wg.Done()
        fmt.Println(n)
    }(i)
}

wg.Wait()  // 等待所有goroutine完成

channel

goroutine间通信,类型安全的管道。

// 创建channel
ch := make(chan int)       // 无缓冲
ch := make(chan int, 10)   // 缓冲大小10

// 发送和接收
ch <- 42      // 发送
value := <-ch // 接收

// 关闭channel
close(ch)

// 接收检查关闭
value, ok := <-ch
if !ok {
    // channel已关闭
}

// range遍历(直到关闭)
for value := range ch {
    fmt.Println(value)
}

// select多路复用
select {
case msg1 := <-ch1:
    fmt.Println("ch1:", msg1)
case msg2 := <-ch2:
    fmt.Println("ch2:", msg2)
case ch3 <- value:
    fmt.Println("sent to ch3")
case <-time.After(time.Second):
    fmt.Println("timeout")
default:
    fmt.Println("no communication")
}

包和可见性

// 包声明(每个文件)
package main

// 导入
import "fmt"
import (
    "fmt"
    "os"
)

// 别名
import f "fmt"
f.Println("hello")

// 点导入(不推荐)
import . "fmt"
Println("hello")  // 直接使用

// 仅初始化
import _ "image/png"  // 执行init函数

// 可见性:大写=导出(public),小写=未导出(private)
type PublicStruct struct {
    ExportedField   int   // 导出
    unexportedField int   // 未导出
}

init函数

包初始化时自动执行,可有多个。

var config Config

func init() {
    // 包初始化
    loadConfig()
}

func init() {
    // 可以有多个init
    setupDatabase()
}

// 执行顺序:
// 1. 导入包的init
// 2. 当前包变量初始化
// 3. 当前包init
// 4. main函数

类型系统

类型定义

// type定义新类型
type MyInt int

var x MyInt = 10
var y int = 10
// x = y  // 错误!不同类型

// type别名(Go 1.9+)
type MyInt2 = int

var a MyInt2 = 10
var b int = 10
a = b  // OK,相同类型

类型嵌入

// 嵌入结构体
type Engine struct {
    Power int
}

func (e *Engine) Start() {
    fmt.Println("Engine started")
}

type Car struct {
    Engine        // 嵌入,继承Engine的字段和方法
    Brand string
}

car := Car{Engine: Engine{Power: 200}, Brand: "Toyota"}
car.Power       // 200(提升字段)
car.Start()     // "Engine started"(提升方法)

// 嵌入接口
type ReadWriter struct {
    Reader
    Writer
}

常见陷阱

// 1. range循环变量陷阱
for i, v := range slice {
    go func() {
        fmt.Println(i, v)  // 错误!闭包捕获变量,值可能变化
    }()
}

// 正确:传参
for i, v := range slice {
    go func(i, v int) {
        fmt.Println(i, v)
    }(i, v)
}

// 2. slice共享底层数组
arr := []int{1, 2, 3, 4, 5}
s1 := arr[0:2]  // [1, 2]
s2 := arr[1:3]  // [2, 3]
s1[1] = 999     // s2[0]也变成999(共享数组)

// 3. map并发读写panic
m := make(map[string]int)
go func() { m["key"] = 1 }()  // 写
go func() { _ = m["key"] }()  // 读(panic!)

// 使用sync.Map或加锁

// 4. nil map不能写入
var m map[string]int  // nil
// m["key"] = 1  // panic
m = make(map[string]int)  // 必须初始化

// 5. slice作为函数参数
func modify(s []int) {
    s[0] = 999  // 影响原slice(引用类型)
    s = append(s, 100)  // 不影响原slice(可能重新分配)
}

最佳实践

  1. 短变量声明:函数内优先用:=

  2. 错误处理:每个error都检查

  3. defer清理资源:文件、锁、连接

  4. 指针接收者:修改对象或大对象

  5. 命名返回值:复杂函数增强可读性

  6. channel通信:不要通过共享内存通信,通过通信共享内存

  7. select处理多channel:超时、取消、多路复用

  8. 空接口慎用:丧失类型安全

  9. gofmt格式化:自动化,无争议

  10. 接口小而专注:io.Reader只有一个方法

核心: Go追求简洁和并发,利用goroutine和channel构建高并发系统。