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

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
Last Week in AI
Last Week in AI
月光博客
月光博客
D
DataBreaches.Net
WordPress大学
WordPress大学
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
Recent Announcements
Recent Announcements
宝玉的分享
宝玉的分享
MyScale Blog
MyScale Blog
C
Check Point Blog
F
Fortinet All Blogs
B
Blog
小众软件
小众软件
Vercel News
Vercel News
罗磊的独立博客
有赞技术团队
有赞技术团队

吴润写字的地方

从零构建 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 使用
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.

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