Skip to content
Go back

用 Go 语言新特性简化代码

Published:  at  07:46 PM
Edit

Go 语言在 1.18 版本之后引入了许多实用的新函数和方法,这些新特性大大简化了我们的日常编码工作。本文将介绍一些最常用的新函数,并通过对比新旧代码来展示它们如何让我们的代码更简洁、更易读。

1. slices 包的新函数

1.1 查找元素

旧版本:

// 查找切片中是否包含某个元素
func contains(slice []int, target int) bool {
    for _, v := range slice {
        if v == target {
            return true
        }
    }
    return false
}

// 使用
numbers := []int{1, 2, 3, 4, 5}
if contains(numbers, 3) {
    // ...
}

新版本:

numbers := []int{1, 2, 3, 4, 5}
if slices.Contains(numbers, 3) {
    // ...
}

1.2 切片排序

旧版本:

// 对切片进行排序
numbers := []int{5, 2, 8, 1, 9}
sort.Ints(numbers)

新版本:

numbers := []int{5, 2, 8, 1, 9}
slices.Sort(numbers)

1.3 切片比较

旧版本:

// 比较两个切片是否相等
func equalSlices(a, b []int) bool {
    if len(a) != len(b) {
        return false
    }
    for i := range a {
        if a[i] != b[i] {
            return false
        }
    }
    return true
}

新版本:

a := []int{1, 2, 3}
b := []int{1, 2, 3}
if slices.Equal(a, b) {
    // ...
}

1.4 遍历切片值

旧版本:

numbers := []int{1, 2, 3, 4, 5}
for i := 0; i < len(numbers); i++ {
    fmt.Println(numbers[i])
}

新版本:

numbers := []int{1, 2, 3, 4, 5}
for v := range slices.Values(numbers) {
    fmt.Println(v)
}

1.5 反向切片遍历

旧版本:

// 反向遍历切片
numbers := []int{1, 2, 3, 4, 5}
for i := len(numbers) - 1; i >= 0; i-- {
    fmt.Println(numbers[i])
}

新版本:

numbers := []int{1, 2, 3, 4, 5}
for _, v := range slices.Backward(numbers) {
    fmt.Println(v)
}

2. strings 包的增强

2.1 字符串裁剪

旧版本:

// 去除字符串两端的空白字符
s := "  hello  "
s = strings.TrimSpace(s)

// 去除字符串两端的特定字符
s = strings.Trim(s, " ")

新版本:

s := "  hello  "
s = strings.TrimSpace(s)  // 和之前功能一样
s = strings.TrimPrefix(s, " ")  // 只去除前缀
s = strings.TrimSuffix(s, " ")  // 只去除后缀

2.2 字符串分割

旧版本:

// 分割字符串
parts := strings.Split("a,b,c", ",")
// 需要手动处理空字符串
if parts[0] == "" {
    parts = parts[1:]
}

新版本:

// 新增了更多分割选项
parts := strings.Split("a,b,c", ",")
// 自动过滤空字符串
parts = strings.Split("a,,b,c", ",")  // ["a", "", "b", "c"]
parts = strings.SplitAfter("a,b,c", ",")  // ["a,", "b,", "c"]

3. maps 包的新函数

3.1 清空 map

旧版本:

// 清空 map
m := map[string]int{"a": 1, "b": 2}
for k := range m {
    delete(m, k)
}

新版本:

m := map[string]int{"a": 1, "b": 2}
maps.Clear(m)

3.2 复制 map

旧版本:

// 复制 map
src := map[string]int{"a": 1, "b": 2}
dst := make(map[string]int, len(src))
for k, v := range src {
    dst[k] = v
}

新版本:

src := map[string]int{"a": 1, "b": 2}
dst := maps.Clone(src)

4. 泛型带来的简化

4.1 通用容器操作

旧版本:

// 为每种类型都需要写一个函数
func maxInt(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func maxFloat64(a, b float64) float64 {
    if a > b {
        return a
    }
    return b
}

新版本:

// 使用泛型,一个函数处理多种类型
func max[T constraints.Ordered](a, b T) T {
    if a > b {
        return a
    }
    return b
}

// 使用
maxInt := max(1, 2)
maxFloat := max(1.5, 2.5)

4.2 通用数据结构

旧版本:

// 需要为每种类型实现一个栈
type IntStack struct {
    items []int
}

func (s *IntStack) Push(x int) {
    s.items = append(s.items, x)
}

func (s *IntStack) Pop() int {
    if len(s.items) == 0 {
        panic("stack is empty")
    }
    x := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return x
}

新版本:

// 使用泛型实现通用栈
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(x T) {
    s.items = append(s.items, x)
}

func (s *Stack[T]) Pop() T {
    if len(s.items) == 0 {
        panic("stack is empty")
    }
    x := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return x
}

// 使用
intStack := &Stack[int]{}
stringStack := &Stack[string]{}

5. 其他实用新特性

5.1 错误处理改进

旧版本:

// 处理多个错误
var err1, err2 error
if err1 != nil {
    return fmt.Errorf("error 1: %v", err1)
}
if err2 != nil {
    return fmt.Errorf("error 2: %v", err2)
}

新版本:

// 使用 errors.Join 合并多个错误
err := errors.Join(err1, err2)
if err != nil {
    return err
}

5.2 时间处理

旧版本:

// 获取当前时间
now := time.Now()
// 需要手动处理时区
loc, _ := time.LoadLocation("Asia/Shanghai")
now = now.In(loc)

新版本:

// 新增了更多便捷的时间函数
now := time.Now().Local()  // 直接获取本地时间

5.3 改进的 HTTP 路由

旧版本:

// 手动实现路由模式匹配
http.HandleFunc("/user/", func(w http.ResponseWriter, r *http.Request) {
    id := strings.TrimPrefix(r.URL.Path, "/user/")
    if id == "" {
        http.Error(w, "Missing user ID", http.StatusBadRequest)
        return
    }
    fmt.Fprintf(w, "User ID: %s", id)
})

新版本:

// 使用增强的 http.ServeMux 支持路径模式
mux := http.NewServeMux()
mux.HandleFunc("GET /user/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    fmt.Fprintf(w, "User ID: %s", id)
})

5.4 新的随机数生成

旧版本:

// 生成随机整数
rand.Seed(time.Now().UnixNano())
n := rand.Intn(100)

新版本:

// 更高效的随机数生成
n := rand.IntN(100)  // 无需显式设置种子

5.5 循环变量捕获

旧版本(可能导致意外行为):

funcs := []func(){}
for i := 0; i < 3; i++ {
    funcs = append(funcs, func() { fmt.Println(i) })
}
for _, f := range funcs {
    f() // 输出 3, 3, 3
}

新版本(Go 1.22 修复):

funcs := []func(){}
for i := 0; i < 3; i++ {
    funcs = append(funcs, func() { fmt.Println(i) })
}
for _, f := range funcs {
    f() // 输出 0, 1, 2
}

Edit

Previous Post
唯一 ID 对比
Next Post
VS Code 配置