标准库最佳实践 - 工程实践指南
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,避免循环拼接
- 切片: 预分配容量,注意引用问题