來自公眾號:新世界雜貨鋪
文章目錄
- 方法集
- 方法呼叫
- 你以為你都懂了
- 彩蛋
作為一個長期從事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握手流程敬請期待,
最后,衷心希望本文能夠對各位讀者有一定的幫助,
注:
- 寫本文時, 筆者所用go版本為: go1.15.2
- 文章中所用完整例子:https://github.com/Isites/go-coder/blob/master/receiver/main.go
參考
https://golang.org/ref/spec#Method_sets
https://golang.org/ref/spec#Calls
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/226932.html
標籤:區塊鏈
上一篇:2020-11-22
