关于golang:一个隐藏在方法集和方法调用中且易被忽略的小细节

35次阅读

共计 2050 个字符,预计需要花费 6 分钟才能阅读完成。

来自公众号:新世界杂货铺

作为一个长期从事 Go 语言开发的程序猿,笔者不敢说本人是老油条但也勉强算一个小油条。然而就在明天,笔者钻研 TLS/SSL 握手源码的时候,忽然灵光一闪,想到了一个和本人认知不符的景象,于是连忙写了一个例子验证一番,后果当头一棒直到码这篇文章时仍旧懵逼。

话不多说,上锤!

不好意思,不是这个锤,是上面这个:

type set interface {set1(s string)
    set2(s string)
}
type test struct {s string}
func (t *test) set1(s string) {t.s = s}
func (t test) set2(s string) {t.s = s}
func main() {
    var (
        t1 test
        t2 = new(test)
    )
    t1.set1("1")
    fmt.Print(t1.s)
    t1.set2("2")
    fmt.Print(t1.s)
    t2.set1("3")
    fmt.Print(t2.s)
    t2.set2("4")
    fmt.Print(t2.s)
    fmt.Print(" ")
    _, ok1 := (interface{}(t1)).(set)
    _, ok2 := (interface{}(t2)).(set)
    fmt.Println(ok1, ok2)
}

正确答案笔者就不间接颁布了,请各位读者急躁在后文寻找答案。

办法集

依据 golang 官网文档晓得,一个类型有一个与之关联的办法集。接口类型的办法集是接口中定义的办法。

官网文档中特地提到,类型 T 的办法集蕴含用 T 申明为 Receiver 的所有办法,而指针类型 \ T 的办法集蕴含用 T 和 \ T 申明的所有办法。

此时,咱们回到下面的例子能够很显著的晓得上面这段代码输入为false true

_, ok1 := (interface{}(t1)).(set)
_, ok2 := (interface{}(t2)).(set)
fmt.Println(ok1, ok2)

T 类型的办法集不蕴含 \* T 类型的办法集,因而 t1 无奈转为 set 接口类型。

事实上,依据这部分官网文档笔者更加纳闷了,因为上述例子能够失常运行,而且类型为 test 的变量调用了 (*test).set1 办法。抱着这样的纳闷笔者疯狂谷狗,最初在 stackoverflow 的领导下发现了这种状况和办法调用无关。

这里特别感谢一下谷狗和 stackoverflow。

办法调用

办法调用笔者在这里仅阐明和本篇相干的内容,其余细节置信各位读者都曾经了然于胸。

上面,先看看官网文档原文:

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x’s method set contains m, x.m() is shorthand for (&x).m()

简略来说,如果 x 可寻址,且 &x 的办法集蕴含 m,则 x.m()(&x).m的缩写。这样后面的例子可能失常运行也在情理之中了。

因而,后面例子的最终输入后果是:1133 false true

如果读者对构造体中的值未产生扭转有纳闷,请参考笔者的这篇文章——[为什么 go 中的 receiver name 不举荐应用 this 或者 self]()。

你认为你都懂了

写完后面的办法集和办法调用笔者细细思考一番,确认没有其余脱漏的细节,于是释怀的上了个厕所。后果厕所还没上完,立马想到一个问题(额定多扯一句,笔者常常在上厕所的时候找到灵感,这可能就是劳逸结合的最佳实际吧):

type los string

func (s los) p1() {fmt.Println(s)
}

func (s *los) p2() {fmt.Println(s)
}

func main() {
    var s1 los = "1111"
    var s2 *los = &s1
    const s3 los = "3333"
    s1.p1()
    s1.p2()
    s2.p1()
    s2.p2()
    s3.p1()
    s3.p2()}

如果你对下面的代码没有任何疑难且认为上述代码可能失常运行,那只能阐明你对本文的浏览还不够认真。

咱们先看看上述代码在 vscode 中的报错。

后面介绍办法调用时,如果 x 可寻址,则 x 能够调用 &x 的类型的办法集。上述代码s3 是常量,是不能够寻址的,因而无奈调用 (*los).p2 办法。

以上,就是笔者已经疏忽的细节,当初回过头来看一看倒也充斥了乐趣。

彩蛋

本篇是钻研 TLS/SSL 握手流程的副产品,因为 TLS/SSL 握手流程笔者还在整顿中,故这篇文章先行一步给个预报,下一期 TLS/SSL 握手流程敬请期待。

最初,衷心希望本文可能对各位读者有肯定的帮忙。

注:

  1. 写本文时,笔者所用 go 版本为: go1.15.2
  2. 文章中所用残缺例子:https://github.com/Isites/go-…

参考

https://golang.org/ref/spec#M…

https://golang.org/ref/spec#C…

正文完
 0