导航菜单

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
}

搜索