循环中遇 error 是否立即返回取决于语义:串行依赖操作应 return err,批量或并发任务需收集所有错误;切忌在 for-range 中修改 slice 导致错误被跳过。

循环中遇到 error 就立即返回,还是继续执行?
Go 没有 try/catch,error 必须显式检查。在循环里,是否中断取决于语义:如果是批量校验或并发任务,通常要收集所有错误;如果是串行依赖操作(如依次写文件、调用下游 API),一个失败往往意味着后续无意义,应提前 return。
常见误写是忽略 err != nil 后的处理逻辑,或者用 log.Fatal 直接退出整个程序——这在库函数或 HTTP handler 里极危险。
- 串行关键路径:遇到第一个
error就return err - 批量非关键操作(如清理临时文件):用切片累积
error,最后统一判断 - 并发场景(
goroutine):必须用sync.WaitGroup+ 错误通道,避免 goroutine 泄漏
用 error 切片收集多个失败(非并发)
适用于「尽力而为」类操作,比如删除一批路径,部分可能已不存在。不能只靠 len(errs) == 0 判断成功——要区分「全成功」和「全跳过」。
var errs []error
for _, path := range paths {
if err := os.Remove(path); err != nil {
// 忽略 "file does not exist",其他错误计入
if !os.IsNotExist(err) {
errs = append(errs, fmt.Errorf("remove %s: %w", path, err))
}
}
}
if len(errs) > 0 {
return fmt.Errorf("failed to remove %d paths: %v", len(errs), errs)
}
for-range 遍历时修改 slice 导致的 error 被跳过
这是隐藏陷阱:在循环中对底层数组做了修改(如 append 或删元素),可能导致下一次迭代读到错误的值,甚至跳过某次 err 检查。
立即学习“go语言免费学习笔记(深入)”;
典型表现是日志里只报了部分错误,但实际有更多失败没被发现。
- 永远不要在
for _, v := range s中修改s的长度或内容 - 需要边遍历边过滤时,用传统
for i := 0; i ,并手动控制索引 - 若必须重建 slice,先收集结果再赋值,而非原地改
defer 在循环里不生效?其实是作用域搞错了
新手常写 for ... { defer f() },以为每次迭代都会注册一个延迟调用——其实 defer 是在当前函数返回时执行,且只绑定到外层函数作用域,循环里的 defer 会全部堆积到最后才跑,参数也全是最后一次迭代的值。
正确做法:把循环体抽成独立函数,让 defer 绑定到该函数的作用域。
for _, file := range files {
if err := processFile(file); err != nil {
return err
}
}
func processFile(name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close() // 这里的 defer 属于 processFile 函数,每次调用都独立生效
// ...
return nil
}
最易被忽略的是:循环中启动 goroutine 时捕获的变量是引用,不是拷贝——哪怕加了 defer,也可能关错文件、读错数据。务必传参,别闭包捕获循环变量。










