这个答案:@christian 的https://stackoverflow.com/a/41816171/10278 为如何模拟“结果计数重载”模式提供了最佳实用建议。
我的目标是解决问题的另一部分——这部分:“但是当我试图找出如何做到这一点时,我什么也没找到”。
下面解释了 Go 类型断言是如何完成的。
Go 中类型断言的调用行为就好像它们基于结果的数量而被重载。
然而,去does not support overloading 的方法和运算符。
查看 Go 的实现,以下是基于结果数量的类型断言似乎被重载的原因:
这种特殊的分派发生在类型断言的内置概念上,因为编译器正在开发出对非内置代码不可用的特殊逻辑。
Go 编译器和运行时是用 Go 编写的。这让我(在某种程度上)很容易发现编译器是解释这种行为的关键。
看看编译器的这一部分:
代码注释已经透露了很多:
// dottype generates SSA for a type assertion node.
// commaok indicates whether to panic or return a bool.
// If commaok is false, resok will be nil.
我们可以通过使用调试器来逐步执行某些类型断言代码。
以this playground snippet 为例。具体来说,这些行:
object_as_closer_hardstop := thing.(io.Closer) // will panic!!
object_as_closer, ok := thing.(io.Closer)
(如果你build Go from source,那么)如果你使用调试器单步执行第一个类型断言,你将在 Go 运行时得到以下代码:
如果你踏入第二个,你最终会:
在第 438 行,您会看到 func assertI2I(只有一个返回值)。稍低一点,在第 454 行,您会看到 assertI2I2。请注意,这两个函数的名称几乎相同,但并不完全相同!
第二个函数的名称末尾有一个2。该函数还有两个返回结果。
正如我们所料:
(查看iface.go 中的函数体,注意其中包含panic。)
assertI2I 和assertI2I2 遵守我们期望的重载规则。如果它们仅在结果数量上有所不同,那么我们这些从源代码编译 Go 的人将无法编译 Go 运行时,因为诸如“assertI2I redeclared”之类的编译器错误。
该语言的用户通常不知道这些内置的运行时函数,因此表面上看,这两行代码似乎调用了同一个函数:
object_as_closer_hardstop := thing.(io.Closer) // will panic!!
object_as_closer, ok := thing.(io.Closer)
但是,在编译时,编译器会根据是否找到“commaok”的情况进行分支:
我们自己的最终用户代码无法修改 Go 的 lexing/parsing/AST-walking 以根据“commaok”调度我们的不同风格的函数。
无论好坏,这就是用户编写的代码无法利用这种模式的原因。