← 标准库概览 | Panic 处理 →

标准库最佳实践 - 工程实践指南

Go 标准库提供了丰富的功能,正确使用标准库可以编写出高效、可维护的代码。本文总结标准库使用的最佳实践和常见陷阱。

错误处理

📝 正确的错误处理

package main

import (
    "errors"
    "fmt"
    "os"
)

// ✅ 推荐:立即处理错误
func readFile(path string) (string, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return "", err // 立即返回
    }
    return string(data), nil
}

// ✅ 推荐:包装错误添加上下文
func processConfig(path string) error {
    data, err := os.ReadFile(path)
    if err != nil {
        return fmt.Errorf("reading config: %w", err) // %w 包装错误
    }
    // ... 处理数据
    return nil
}

// ✅ 推荐:使用 errors.Is/As 检查错误
func handleError(err error) {
    if errors.Is(err, os.ErrNotExist) {
        fmt.Println("File does not exist")
    }
    
    var pathErr *os.PathError
    if errors.As(err, &pathErr) {
        fmt.Printf("Path error: %s\n", pathErr.Path)
    }
}

// ❌ 不推荐:忽略错误
func badExample() {
    data, _ := os.ReadFile("config.json") // 忽略错误!
    _ = data
}

func main() {
    data, err := readFile("config.json")
    if err != nil {
        handleError(err)
    }
    fmt.Println(data)
}

资源管理

📝 使用 defer 管理资源

package main

import (
    "fmt"
    "os"
)

// ✅ 推荐:defer 关闭资源
func processFile(path string) error {
    file, err := os.Open(path)
    if err != nil {
        return err
    }
    defer file.Close() // 确保关闭
    
    // 处理文件...
    return nil
}

// ✅ 推荐:多个 defer 按顺序执行
func multipleResources() error {
    f1, _ := os.Open("file1.txt")
    defer f1.Close()
    
    f2, _ := os.Open("file2.txt")
    defer f2.Close()
    
    // defer 按 LIFO 顺序执行:f2.Close() 先于 f1.Close()
    return nil
}

// ✅ 推荐:defer 在循环外
func processFiles(paths []string) error {
    for _, path := range paths {
        file, err := os.Open(path)
        if err != nil {
            return err
        }
        defer file.Close() // 所有 defer 在函数返回时执行
        
        // 处理文件...
    }
    return nil
}

// ❌ 不推荐:defer 在循环内 (资源泄漏)
func badDeferInLoop(paths []string) error {
    for _, path := range paths {
        file, _ := os.Open(path)
        defer file.Close() // 循环多次会累积 defer
    }
    return nil
}

并发安全

📝 并发安全实践

package main

import (
    "sync"
)

// ✅ 推荐:使用 Mutex 保护共享数据
type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *SafeCounter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

// ✅ 推荐:使用 sync.Once 初始化
type Singleton struct {
    data string
}

var (
    instance *Singleton
    once     sync.Once
)

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{data: "singleton"}
    })
    return instance
}

// ✅ 推荐:使用 channel 通信
func worker(jobs <-chan int, results chan<- int) {
    for job := range jobs {
        results <- job * 2
    }
}

字符串操作

📝 高效字符串操作

package main

import (
    "bytes"
    "fmt"
    "strings"
)

// ✅ 推荐:使用 strings.Builder
func buildString(n int) string {
    var sb strings.Builder
    for i := 0; i < n; i++ {
        fmt.Fprintf(&sb, "Item %d\n", i)
    }
    return sb.String()
}

// ✅ 推荐:使用 bytes.Buffer
func buildBytes(n int) []byte {
    var buf bytes.Buffer
    for i := 0; i < n; i++ {
        buf.Write([]byte("data"))
    }
    return buf.Bytes()
}

// ✅ 推荐:使用 strings.Join
func joinStrings(items []string) string {
    return strings.Join(items, ", ")
}

// ❌ 不推荐:循环拼接字符串
func badConcat(n int) string {
    result := ""
    for i := 0; i < n; i++ {
        result += fmt.Sprintf("Item %d\n", i) // O(n²) 性能
    }
    return result
}

切片操作

📝 切片最佳实践

package main

// ✅ 推荐:预分配容量
func createSlice(n int) []int {
    slice := make([]int, 0, n) // 预分配容量
    for i := 0; i < n; i++ {
        slice = append(slice, i)
    }
    return slice
}

// ✅ 推荐:使用 copy 复制切片
func copySlice(src []int) []int {
    dst := make([]int, len(src))
    copy(dst, src)
    return dst
}

// ✅ 推荐:切片引用注意
func sliceReference() {
    data := []int{1, 2, 3, 4, 5}
    
    // sub 仍然引用原底层数组
    sub := data[:3]
    
    // 如果需要独立切片
    independent := copySlice(data[:3])
    _ = independent
}

// ❌ 不推荐:未预分配容量
func badSlice(n int) []int {
    slice := []int{} // 每次 append 可能重新分配
    for i := 0; i < n; i++ {
        slice = append(slice, i)
    }
    return slice
}

最佳实践总结

✅ 核心要点

  • 错误处理: 立即处理,使用%w 包装,errors.Is/As 检查
  • 资源管理: 使用 defer 关闭,避免循环内 defer
  • 并发安全: Mutex 保护共享数据,sync.Once 初始化
  • 字符串: 使用 strings.Builder,避免循环拼接
  • 切片: 预分配容量,注意引用问题