注意:Go 1.18(2022 年第一季度)确实将 interface{} 重命名为 any(interface{} 的别名)。
请参阅issue 49884、CL 368254 和 commit 2580d0e。
请参阅此答案的最后一部分。
可以参考文章“How to use interfaces in Go”(基于“Russ Cox’s description of interfaces”):
什么是接口?
一个接口是两个东西:
interface{}类型(或Go 1.18+的any),空接口是没有方法的接口。
由于没有 implements 关键字,所有类型都至少实现零个方法,并且满足一个接口是自动完成的,所有类型都满足空接口。
这意味着,如果您编写一个将 interface{} 值作为参数的函数,您可以为该函数提供任何值。
(这就是Msg 在您的问题中所代表的含义:任何值)
func DoSomething(v interface{}) {
// ...
}
func DoSomething(v any) {
// ...
}
这就是令人困惑的地方:
在DoSomething 函数内部,v 的类型是什么?
新手地鼠被引导相信“v 是任何类型”,但这是错误的。
v 不是任何类型; 它是interface{} 类型。
将值传递给DoSomething 函数时,Go 运行时将执行类型转换(如有必要),并将值转换为interface{} 值.
所有值在运行时都只有一种类型,v 的一种静态类型是 interface{}(或在 Go 1.18+ 中使用 any)。
一个接口值由两个数据字构成:
- 一个词用于指向值的基础类型的方法表,
- 另一个词用于指向该值所保存的实际数据。
附录:这是 Russ 的关于接口结构的文章相当完整:
type Stringer interface {
String() string
}
接口值表示为两个字对,给出一个指向接口中存储的类型信息的指针和一个指向相关数据的指针。
将 b 分配给 Stringer 类型的接口值会设置接口值的两个字。
接口值中的第一个词指向我所说的接口表或 itable(读作 i-table;在运行时源代码中,C 实现名称是 Itab)。
itable 以有关所涉及类型的一些元数据开始,然后成为函数指针列表。
请注意,itable 对应于接口类型,而不是动态类型。
在我们的示例中,Stringer 持有类型 Binary 的 itable 列出了用于满足 Stringer 的方法,即 String:Binary 的其他方法(Get)在 itable 中没有出现。
接口值中的第二个字指向实际数据,在本例中是b的副本。
赋值var s Stringer = b 复制b 而不是指向b,原因与var c uint64 = b 复制的原因相同:如果b 稍后更改,s 和c 应该具有原始价值,而不是新价值。
存储在接口中的值可能是任意大的,但只有一个字专门用于保存接口结构中的值,因此赋值会在堆上分配一块内存并将指针记录在一个字槽中。
Issue 33232 似乎在 Go 1.18(2022 年第一季度)中指出 any 作为 interface{} 的别名
Russ Cox explains:
-
'any' 仅用于约束是每个泛型文章(书籍、博客文章等)中都会出现的细节。
如果我们认为我们最终可能会允许它,那么从一开始就允许它并避免使所有书面材料无效是有意义的。
-
'any' 仅用于约束是一个意外的删减,它降低了概念的通用性和正交性。
说“让我们拭目以待”很容易,但规定用途往往会产生比完全概括更多的参差不齐的特征。我们在类型别名中也看到了这一点(谢天谢地,我们几乎拒绝了所有提议的删减)。
-
如果在泛型中允许使用“any”,但在非泛型代码中不允许使用,那么它可能会鼓励人们过度使用泛型,仅仅是因为“any”比“interface{}”写得更好,当决定仿制药与否真的应该考虑其他因素。
-
如果我们也允许 'any' 用于普通的非泛型用法,那么在代码中看到 interface{} 可以作为一种信号,表明该代码早于泛型并且在后泛型世界中尚未被重新考虑.
一些使用interface{} 的代码应该使用泛型。其他代码应该继续使用接口。
以一种或另一种方式重写它以删除文本“interface{}”会给人们一种清晰的方式来查看他们更新的内容和没有更新的内容。 (当然,出于向后兼容的原因,一些使用泛型可能更好的代码仍必须使用interface{},但仍可以对其进行更新以确认已考虑并做出决定。)
该线程还包括一个explanation about interface{}:
这不是特殊设计,而是 Go 类型声明语法的逻辑结果。
您可以使用具有多个方法的匿名接口:
func f(a interface{Foo(); Bar()}) {
a.Foo()
a.Bar()
}
类似于如何在需要类型的任何地方使用匿名结构:
func f(a struct{Foo int; Bar string}) {
fmt.Println(a.Foo)
fmt.Println(a.Bar)
}
一个空接口恰好匹配所有类型,因为所有类型至少有零个方法。
如果您想保持一致/不想引入特殊情况,删除 interface{} 意味着从语言中删除所有界面功能。