五个让你的Go代码更优雅的实用技巧
Go语言以简洁著称,但写出真正优雅的Go代码仍需一些功力。分享几个我常用的技巧。
1. 巧用sync.Once实现单例
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{
}
})
return instance
}
sync.Once确保初始化代码只执行一次,且线程安全,比传统的双重检查锁更简洁。
2. context超时控制的正确姿势
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // 务必调用释放资源
select {
case <-ctx.Done():
return ctx.Err()
case result := <-doWork():
return result, nil
}
忘记defer cancel()会造成资源泄漏,这是新手常踩的坑。
3. 用errgroup管理并发任务
import "golang.org/x/sync/errgroup"
g := new(errgroup.Group)
g.Go(func() error {
return fetchAPI1() })
g.Go(func() error {
return fetchAPI2() })
if err := g.Wait(); err != nil {
// 任一任务失败即返回
}
相比手动管理sync.WaitGroup,errgroup优雅地集成了错误处理。
4. 空结构体的妙用
// 用作集合
set := map[string]struct{
}{
}
set["key"] = struct{
}{
}
_, exists := set["key"]
// 用作信号通道
done := make(chan struct{
})
struct{}不占用内存空间,是表达“存在性”和“事件通知”的最佳选择。
5. 接口隔离与依赖注入
type Storage interface {
Save(data []byte) error
}
type UserService struct {
storage Storage // 依赖接口而非具体实现
}
面向接口编程让代码可测试、可替换,这是Go工程化的基石。
总结:优雅的Go代码不在技巧多华丽,而在于恰到好处的简洁。多读标准库源码,你会发现更多精妙设计。