惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
H
Help Net Security
小众软件
小众软件
N
Netflix TechBlog - Medium
C
Check Point Blog
量子位
Last Week in AI
Last Week in AI
GbyAI
GbyAI
Martin Fowler
Martin Fowler
M
MIT News - Artificial intelligence
博客园 - 聂微东
Engineering at Meta
Engineering at Meta
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
D
DataBreaches.Net
Project Zero
Project Zero
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
Security Latest
Security Latest
Cisco Talos Blog
Cisco Talos Blog
Recorded Future
Recorded Future
I
Intezer
L
Lohrmann on Cybersecurity
Cyberwarzone
Cyberwarzone
博客园_首页
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LangChain Blog
P
Palo Alto Networks Blog
V
V2EX
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
The Exploit Database - CXSecurity.com
The Hacker News
The Hacker News
Blog — PlanetScale
Blog — PlanetScale
G
GRAHAM CLULEY
T
The Blog of Author Tim Ferriss
C
Cisco Blogs
The Register - Security
The Register - Security
L
LINUX DO - 热门话题
P
Privacy & Cybersecurity Law Blog
Scott Helme
Scott Helme
F
Full Disclosure
博客园 - 司徒正美
Recent Announcements
Recent Announcements
IT之家
IT之家
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Attack and Defense Labs
Attack and Defense Labs
Cloudbric
Cloudbric
Help Net Security
Help Net Security
The Last Watchdog
The Last Watchdog

涛叔

自用 Claude Code 拼车中转服务 欢迎使用 @ZZ.AC 邮箱服务 我在用的 VPS 云主机 实现备份3-2-1原则 玩转 ZZ.AC 域名 ZZ.NIC 公益 Linux 主机空间 使用 Nginx 构建计费 DoH 代理服务 通过 Let’s Encrypt 为 IP 地址签发免费 SSL 证书
Go语言接口的空指针问题
2026-01-24 · via 涛叔

最近在调试某 Go 语言源项目的时候,发现某个变量值为 nil,但是 if v != nil 检测居然不通过😮这还是头一回遇到。一番折腾后发现接口变量的空指针跟普通的空指针还是有区别的。本文梳理相关研究内容作个备忘,也分享给有需要的读者。

先给出一段代码:

package main

import "fmt"

func main() {
  var j *int = nil
  var k any = j
  
  fmt.Printf("%v,%v\n", k, k == nil)
}

请问程序执行之后输出什么结果🧐

答案是<nil>,false

慢着,不是变量k的值已经输出是<nil>了,怎么后面的k == nil结果还是false呢?这不是自相矛盾吗?

我们回到代码中。这里的变量k类型为any,即interface{},可以保存任意变量。而变量j为整型指针,它的值为nil

从直觉上看,因为j的值是nil,所以我们自然希望把j赋给k后,k的值也是nil。而前面输了结果中的第一部分为<nil>,在某种程序上「符合」我们的直觉。所以后面的 false才会更显诡异。

回到接口(interface)的本质,接口变量同时持有保存对象的地址和类型。要想让k == nil 结果为true,就需要该接口变量保存的地址和类型同时为空才可以!在前例中,虽然变量 j的值已经是nil了,但赋值给变量k之后,k同时还保存了*int类型信息,所以它不是nil

但为什么前面的代码中还会输出<nil>呢,那是因为fmt.Printf()会自动使用反射提取接口变量指向的变量,再输出目标变量的值,结果自然是nil了。

为此,我们可以运行以下代码来验证:

package main

import "fmt"

func main() {
  var k any = nil
  
  fmt.Printf("%v,%v\n", k, k == nil)
}

不出所料,结果是<nil>,true

一句话总结,对于 interface 变量,只要不是直接将 nil 赋值给他,任何其他赋值后该接口变量的值都不是nil!

好像也没有很麻烦,这有什么用呢?

这里是另外一段示例代码:

package main

import "fmt"

type Animal interface {
  speak()
}

type Dog struct{}

func (d Dog) speak() {
  fmt.Println("Woof!")
}

func print(a Animal) {
  if a != nil {
    a.speak()
  } else {
    fmt.Println("Dog is not present")
  }
}

func main() {
  var a *Dog = nil

  print(a)
}

这是一个典型的接口范例。代码先定义Animal接口,要求实现speak()方法。然后又定义了Dog类型,实现了Animal接口。接着又在print(a Animal)函数中接收动物接口实现,再根据变量值判断是否需要调用speak()方法。

函数print(a Animal)显然希望跳过空指针场景。但实际运行就会报如下错误:

panic: value method main.Dog.speak called using nil *Dog pointer

显然if a != nil {检查没有成功,也就是说变量a的值不为空,它的值是在调用print 函数传参时隐性赋予的,值为空的且类型为*Dog的指针。根据前文的分析,有类型的空指针不是空指针🤦‍♂️所以报错。

那要怎么做才能避免出现这类问题呢?

一种方法是避免使用指针,也就不存在传nil的情况,直接传值变量,简单粗暴!

另一个办法是利用反射,动态检查接口变量底层值是否为指针:

if h == nil || reflect.ValueOf(h).IsNil() {
  // ...
}

但使用反射可能存存性能问题,大家要注意。