Go errors.As 目标参数为什么必须是非空指针
来源:17golang原创
时间:2026-09-27 21:37:28 458浏览 收藏
errors.As 的 target 必须是非空指针,因为它不只是检查类型,还要把错误树中找到的具体错误写进调用者提供的变量。最容易误解的一点是:目标变量本身可以是 nil,不能为 nil 的是传给 errors.As 的外层地址。
官方文档:https://pkg.go.dev/errors#As
var target *MyError初始为 nil 没问题,调用时应传&target。target必须是非 nil 指针,且它指向的类型要实现error,或者本身是接口类型。errors.As会检查整棵错误树,并把首个可赋值的匹配值写回目标变量。- 目标参数形状错误属于程序员错误,因此标准库选择 panic,而不是返回 false。
先用最小实验看懂双指针
我第一次写 errors.As 时也觉得 &target 多了一层。真正理解后就很简单:错误值通常是 *ValidationError,而目标变量也要保存这个指针;为了让函数修改目标变量,就必须再传目标变量自己的地址,于是参数类型自然变成 **ValidationError。
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "invalid field: " + e.Field
}
func validate() error {
// 用 %w 保留底层错误,使 errors.As 能继续检查错误树
return fmt.Errorf("save config: %w", &ValidationError{Field: "port"})
}
func main() {
err := validate()
var target *ValidationError // 目标值可以暂时是 nil
// &target 是非 nil 的可写槽位,匹配值会写回 target
if errors.As(err, &target) {
fmt.Println(target.Field)
}
}

三个条件中任何一个不满足都会 panic
官方文档给出的约束可以拆成三项。第一,传入值必须是指针;第二,这个指针不能为 nil;第三,指针指向的类型必须实现 error,或者是任意接口类型。下面几种写法看似相近,运行意义完全不同:
var pointerTarget *ValidationError
// 错误:传入的是 nil 的 *ValidationError,不是目标变量的地址
// errors.As(err, pointerTarget)
var valueTarget ValidationError
// 错误:传入的是普通结构体,errors.As 没有位置写回结果
// errors.As(err, valueTarget)
// 正确:&pointerTarget 是 **ValidationError,提供了可写槽位
if errors.As(err, &pointerTarget) {
fmt.Println(pointerTarget.Field)
}
这也是为什么参数错误会 panic:false 应只表示“错误树里没有匹配类型”,不能同时表示“调用方式本身无效”。把两类问题分开后,测试失败时更容易定位。
匹配发生在错误树,结果写进目标变量
errors.As 会先检查根错误,再深度优先检查由 Unwrap() error 或 Unwrap() []error 暴露的子节点。某个具体错误可赋值给目标指向的类型时就匹配成功;如果错误类型自定义了 As(any) bool,也可以参与匹配。找到首个匹配后,标准库把该值写入目标变量并返回 true。

接口目标也遵循同一规则,只是目标变量声明为接口。例如要识别可报告临时状态的错误,可以先定义最小接口,再传它的地址:
type temporary interface {
Temporary() bool
}
var target temporary
// 指向接口变量的非 nil 指针同样可以接收匹配值
if errors.As(err, &target) && target.Temporary() {
fmt.Println("retry later")
}
新版 Go 可以考虑 errors.AsType
在提供 errors.AsType 的 Go 版本中,大多数只想取得具体错误值的场景可以直接接收返回值,不再手动准备双指针。它仍然检查错误树和自定义 As 方法,但把类型参数和返回值放在调用表达式里,误传 nil target 的机会更少。
// 泛型参数直接表达要查找的错误类型,返回值就是匹配结果
if target, ok := errors.AsType[*ValidationError](err); ok {
fmt.Println(target.Field)
}
如果项目需要兼容较早的 Go 工具链,继续使用 errors.As 即可。我的做法是把 var target *T 与 errors.As(err, &target) 紧挨着写,并在测试中覆盖直接错误、单层包装、多层包装和无匹配四种情况。
target 变量为 nil 为什么还能成功?
因为 nil 是 *ValidationError 的零值,而传入的 &target 是一个真实、非 nil 的地址,函数可以把匹配到的指针写进去。
能不能传 new(ValidationError)?
这只是 *ValidationError,而指针错误类型通常需要 **ValidationError 作为写回位置。最清楚的写法仍是先声明目标变量,再传它的地址。
errors.As 返回 false 后能使用 target 吗?
不应依赖它。把目标变量视为只有在返回 true 时才有效,可以避免复用旧变量时误读残留值。
-
364 收藏
-
301 收藏
-
421 收藏
-
261 收藏
-
330 收藏
-
357 收藏
-
225 收藏
-
301 收藏
-
389 收藏
-
225 收藏
-
240 收藏
-
497 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习