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

推荐订阅源

A
About on SuperTechFans
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
C
Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
A
Arctic Wolf
Scott Helme
Scott Helme
P
Palo Alto Networks Blog
S
Schneier on Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tor Project blog
量子位
G
Google Developers Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog RSS Feed
NISL@THU
NISL@THU
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
AWS News Blog
AWS News Blog
爱范儿
爱范儿
Last Week in AI
Last Week in AI
Y
Y Combinator Blog
L
LINUX DO - 最新话题
Security Archives - TechRepublic
Security Archives - TechRepublic
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Secure Thoughts
Cloudbric
Cloudbric
aimingoo的专栏
aimingoo的专栏
L
Lohrmann on Cybersecurity
TaoSecurity Blog
TaoSecurity Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Hacker News: Ask HN
Hacker News: Ask HN
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
有赞技术团队
有赞技术团队
S
Security @ Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
G
GRAHAM CLULEY
P
Proofpoint News Feed
V
V2EX
Martin Fowler
Martin Fowler
C
CERT Recently Published Vulnerability Notes
Attack and Defense Labs
Attack and Defense Labs
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Cloudflare Blog
SecWiki News
SecWiki News
罗磊的独立博客
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
小众软件
小众软件
The Last Watchdog
The Last Watchdog

吴润写字的地方

从零构建 A 股 ETF 动量轮动系统:一个量化新手的完整指南 2025 年 8 月 Digest 2025 年 7 月 Digest 2025 年 6 月 Digest 2025 年 5 月 Digest 2025 年 4 月 Digest 清明桐庐行 2025 年 3 月 Digest 2024 年终总结 换车 崇明岛一日游 夜游徐汇滨江绿地 2023 年终总结 踏春之浦康休闲公园 香港澳门行 读「投资第一课」有感 皮皮玩球 Chrome 插件推荐 Go 并发之道:并发介绍 The Night Agent 我是如何使用 ChatGPT 的 2022 年终总结 在 Ubuntu 上搭建 MinIO 2021 年终总结 Golang 的 defer 关键字使用注意事项 恭王府 强制删除 k8s terminating 状态命名空间 PromQL 计算 CPU 使用率 PromQL 如何计算变化(率) Trickster 使用 奥森的向日葵 Go Modules - checksum mismatch 错误解决 CDN 使用初探 k8s 介绍 再探 798 解决 gin 路由冲突问题 wu.run 域名正式使用 GoLand 使用指南 macOS 软件推荐 2020 这一年 Python 循环列表删除元素的注意事项 Mac 下的定时任务工具:Launchctl Linux 服务器搭建 VNC 可视化桌面 Jenkins 个别 job 页面打开超时的解决方法 遇见 Aamir Khan Hexo 从 GitHub 到阿里云 『从未说出口的秘密』词云分析 768 的秋天 北戴河 Outing 飞盘记 三里屯夜景 爬虫图解:轮子哥关注了哪些人 2017-06-30 GitHub+Hexo 搭建个人网站详细教程 特朗普税改 如果有一天,苹果和微信只能选一个,你会选哪一个? About | 吴润写字的地方 Links | 吴润写字的地方 Memos | 吴润写字的地方
Golang 不可寻址的理解
2021-11-12 · via 吴润写字的地方
Table of Contents

如果没有听过 Golang 的不可寻址(not addressable)相关概念,没有关系,从字面上看,就是无法获取值的地址。先举个简单的例子:

package main

import "fmt"

func main() {
    m := map[int]string{0: "hello"}
    fmt.Printf("%p", &m[0])
}

mmap[int]string 类型,只包含一个键值对,打印这个键值对的值的地址,直接编译错误:cannot take the address of m[0],无法获取 map 中值的地址。这是个比较常见的不可寻址的例子。原因也比较简单,map 类型是通过哈希表实现的,随着 map 元素的增多,可能触发扩容,那么 map 的值的位置发生改变,即其地址会发生变化,所以无法对 map 的值寻址。另一方面,如果元素不存在于map 中,返回零值,而零值是不可变对象,是不能寻址的(golang 中不可变对象是不可寻址的,如常量)。

再看个稍微复杂的例子,在Golang wiki 的 MethodSets 有这样一句话:

The concrete value stored in an interface is not addressable, in the same way that a map element is not addressable.

意思具体值赋值给 interface 类型后与 map 中的元素一样是不可寻址的。先不去理解这句话,先看个例子:

package main

const NewName = "run.wu"

type Male struct {
	Name string
}

func (m Male) getName() string {
	return m.Name
}

func (m *Male) setName(name string) {
	m.Name = name
}

func main() {
	m1 := Male{}
	m1.getName()
	m1.setName(NewName)

	m2 := &Male{}
	m2.getName()
	m2.setName(NewName)
}

定义了一个 Male 类型,有两个方法,一个是值接收者的 getName() 方法,一个是指针接收者 setName() 方法。在 main() 中,m1Male 类型的值对象,m2Male 类型的指针对象,在这两个对象上都调用了 getName()setName() 方法,可以正常调用执行。那么问题来了:

  • m1 是值类型对象(调用者),为什么可以调用指针接收者方法 setName()
  • m2 是指针类型对象(调用者),什么可以调用值接收者方法 getName()?

对于第一个问题,值调用者调用指针接收者方法时, 编译器默认会使用调用者的引用(取地址)来调用方法,即编译器隐示转换 成 (&m1).setName() 对于第二个问题,指针调用者调用值接收者方法时,编译器默认会将指针调用者解引用(取值)为值类型,即编译器隐示转换成 (*m2).getName()

上面的例子还是比较好理解的,很多书和文章都有解释这个问题。

那再看另一个例子:

package main

const NewName = "run.wu"

type Person interface {
	getName() string
	setName(name string)
}

type Male struct {
	Name string
}

func (m Male) getName() string {
	return m.Name
}

func (m *Male) setName(name string) {
	m.Name = name
}

func main() {
	var p1 Person = Male{} // error
	p1.getName()
	p1.setName(NewName)

	var p2 Person = &Male{}
	p2.getName()
	p2.setName(NewName)
}

这个例子和刚刚的例子很类似,不同点的是,增加了一个 Person 类型的接口,定义了 Male 类型,实现了值接收者的 getName() 方法,实现了指针接收者的 setName() 方法。

初始化 Male 的值对象赋值给 Person 接口,记作 p1,直接报错:

cannot use Male literal (type Male) as type Person in assignment:
	Male does not implement Person (setName method has pointer receiver)

错误内容是,Male 类型的变量不能赋值给 Person,因为 Male 类型没有实现 Person 接口(setName 是指针接收者方法)。

那为什么之前的例子中,编译器可以自动将值类型(非接口类型)取地址做隐示转换,而这里就不可以了?原因就是开头那就话:

The concrete value stored in an interface is not addressable.

值类型赋值给接口,是不可寻址的,既然不可寻址,编译器也就没办法自动取其地址传给指针接收的方法了。