Go接口方法必须返回error且置于最后,调用后须先检查err再使用其他返回值,包装错误应使用%w保留原始堆栈。

Go 接口方法返回 error 是标准做法,不是可选项
Go 语言中,任何可能失败的接口方法都应显式返回 error。这不是风格偏好,而是类型系统强制要求的契约——调用方必须检查、处理或传递该错误。忽略 err 不仅违反 Go 的错误处理哲学,还会让 panic 隐蔽发生(比如后续对 nil 结果解引用),或掩盖上游服务的真实故障状态。
定义接口时 error 必须作为最后一个返回值
Go 社区约定和工具链(如 golint、go vet)默认 expect error 是函数/方法的最后一个返回值。接口定义若违背此约定,会导致实现方难以适配,且与标准库(如 io.Reader.Read、http.Client.Do)不一致。
type UserService interface {
GetUser(id int) (User, error) // ✅ 正确:error 在最后
CreateUser(u User) (int, error) // ✅ 正确:多值 + error
DeleteUser(id int) error // ✅ 正确:仅 error
// Bad: DeleteUser(id int) (error, bool) —— 工具会警告,调用方易错判
}
调用接口方法后必须检查 error,不能只看非 error 返回值
常见错误是“先用结果变量,再判断 err”,尤其在结构体字段访问或方法链调用时。一旦 err != nil,其他返回值处于未定义状态(通常是零值),直接使用会引入静默逻辑错误。
- ❌ 错误写法:
user, err := svc.GetUser(123) if user.Name != "" { ... } // user 是零值,Name == "" 为真,但不代表成功 - ✅ 正确写法:
user, err := svc.GetUser(123) if err != nil { log.Printf("failed to get user 123: %v", err) return err // 或返回自定义错误、fallback 值等 } // 此时 user 才可信 fmt.Println(user.Name) - 注意:即使接口返回指针(如
*User, error),err != nil时该指针也可能是nil,不可解引用
包装 error 时优先用 fmt.Errorf("%w", err),避免丢失原始堆栈
当接口调用失败需向上层透传时,简单拼接字符串(如 fmt.Errorf("get user failed: %v", err))会切断错误链,导致无法用 errors.Is 或 errors.As 判断底层错误类型(比如是否是网络超时)。Go 1.13+ 的错误包装机制要求显式使用 %w 动词。
立即学习“go语言免费学习笔记(深入)”;
// 调用下游 HTTP 接口
resp, err := http.DefaultClient.Do(req)
if err != nil {
// ❌ 丢失原始 err 类型和堆栈
// return fmt.Errorf("http request failed: %v", err)
// ✅ 保留错误链,支持 errors.Is(err, context.DeadlineExceeded)
return fmt.Errorf("http request failed: %w", err)
}
如果需要添加上下文但不希望暴露敏感信息(如 URL、参数),可先用 errors.Unwrap 提取原始错误再包装,或用自定义 error 类型封装。
fmt.Errorf("not found"),有的返回 sql.ErrNoRows,上层就很难统一做重试或降级。error 类型和语义必须由接口契约明确定义。










