nil interface
🔴 困难题目描述
以下代码的输出是什么?为什么?
type MyError struct{}
func (e *MyError) Error() string {
return "error"
}
func returnsError() error {
var p *MyError = nil
return p
}
func main() {
err := returnsError()
fmt.Println(err == nil) // 输出?
fmt.Println(err) // 输出?
}期望输出
false
<nil>提示
- interface 包含类型和值两部分
- nil interface 需要类型和值都为 nil
解法
参考答案 (3 个标签)
interface 底层结构 nil
核心概念
interface 的底层结构:
// 非空接口
type iface struct {
tab *itab // 方法表(类型信息)
data unsafe.Pointer // 数据指针
}
// 空接口(interface{})
type eface struct {
_type *_type // 类型信息
data unsafe.Pointer // 数据指针
}图解
真正的 nil interface:
┌──────────────────────┐
│ iface │
├──────────────────────┤
│ tab = nil │ 类型为 nil
│ data = nil │ 值为 nil
└──────────────────────┘
err == nil ✓
returnsError() 返回的 interface:
┌──────────────────────┐
│ iface │
├──────────────────────┤
│ tab = *MyError │ 类型不为 nil
│ data = nil │ 值为 nil
└──────────────────────┘
err == nil ✗(因为类型不为 nil)代码分析
func returnsError() error {
var p *MyError = nil // p 是 nil 指针
return p // 返回接口
}
// 等价于:
func returnsError() error {
var p *MyError = nil
return error(p) // 类型转换
}
// 实际返回:
// {
// tab: *MyError 类型信息,
// data: nil
// }正确的返回 nil 方式
// ✅ 方式 1:直接返回 nil
func returnsError() error {
return nil
}
// ✅ 方式 2:返回 nil 指针,但不要赋值给 interface 变量
func returnsError() error {
var p *MyError
if someCondition {
return p // 危险!返回的不是 nil interface
}
return nil // 正确
}
// ✅ 方式 3:明确判断
func returnsError() error {
var p *MyError = nil
if p == nil {
return nil
}
return p
}检查 nil interface
func check(err error) {
// ❌ 错误:直接判断
if err == nil {
fmt.Println("is nil")
}
// ✅ 正确:使用反射或类型断言
if err == nil || (reflect.ValueOf(err).IsNil() && err != nil) {
fmt.Println("is nil")
}
}扩展:interface 的其他陷阱
陷阱 1:interface 比较失败
func main() {
var err1 error = (*MyError)(nil)
var err2 error = (*MyError)(nil)
fmt.Println(err1 == err2) // false
// 原因:虽然类型相同,但 data 不同(都是 nil,但指针地址不同)
}
func main() {
var err1 error = (*MyError)(nil)
var err2 error = nil
fmt.Println(err1 == err2) // false
// 原因:err1 类型不为 nil,err2 类型为 nil
}陷阱 2:nil slice vs nil interface
func main() {
var s []int = nil
var err error = s
fmt.Println(err == nil) // false
fmt.Println(s == nil) // true
// 原因:err 的类型是 []int,不是 nil
}陷阱 3:函数返回 interface
func getValue() interface{} {
var p *MyError = nil
return p // 返回的不是 nil interface
}
func main() {
v := getValue()
fmt.Println(v == nil) // false
}