為什么go中的receiver name不推薦使用this或者self
回復(fù)“Go語言”即可獲贈從入門到進(jìn)階共10本電子書
前言
在日常的開發(fā)中我們除了定義函數(shù)以外, 我們還會定義一些方法。這本來沒有什么, 但是一些從PHP或者其他面向?qū)ο笳Z言轉(zhuǎn)GO的同學(xué)往往會把receiver name命名為this,?self,?me等。
筆者在實(shí)際項(xiàng)目開發(fā)中也遇到類似的同學(xué), 屢次提醒卻沒有效果,于是決心寫下這篇文章以便好好說服這些同學(xué)。
CR標(biāo)準(zhǔn)做法
首先我們來看一下GO推薦的標(biāo)準(zhǔn)命名Receiver Names,以下內(nèi)容摘抄自https://github.com/golang/go/wiki/CodeReviewComments#receiver-names:
The name of a method's receiver should be a reflection of its identity;
often a one or two letter abbreviation of its type suffices (such as "c" or "cl" for "Client").
Don't use generic names such as "me", "this" or "self", identifiers typical of object-oriented languages that gives the method a special meaning.
In Go, the receiver of a method is just another parameter and therefore, should be named accordingly.
...
簡單翻譯總結(jié)有如下2點(diǎn):
方法接受者名稱應(yīng)反映其身份, 并且不要使用
me,?this,?self這些面向?qū)ο笳Z言的典型標(biāo)志符。在go中方法接受者其實(shí)就是方法的另一個(gè)參數(shù)。
Receiver是方法的第一個(gè)參數(shù)!
上面的第二點(diǎn), 可能不是很好理解,所以我們直接看下面的demo:
// T ...
type T int
// Println ...
func (t T) Println() {
fmt.Println("value: %v", t)
}
func main() {
t := T(1)
t.Println()
T.Println(t)
// receiver作為函數(shù)的第一個(gè)參數(shù),這個(gè)時(shí)候發(fā)生值拷貝,所以方法內(nèi)部的t變量是真實(shí)t變量的一個(gè)拷貝
// 這和this的含義是不相符的}
// output:
value: 1
value: 1
通過上面的demo, 我們知道接受者可以直接作為第一個(gè)參數(shù)傳遞給方法的。而t.Println()應(yīng)該就是Go中的一種語法糖了。
到這里可能有同學(xué)又要問了, 既然Go提供了這種語法糖,那我們這樣命名有什么問題呢?筆者先不著急解釋, 我們繼續(xù)看下面的demo:
// Test ...
type Test struct {
A int
}
// SetA ...
func (t Test) SetA(a int) {
t.A = a
}
// SetA1 ...
func (t *Test) SetA1(a int) {
t.A = a
}
func main() {
t := Test{
A: 3,
}
fmt.Println("demo1:")
fmt.Println(t.A)
t.SetA(5)
fmt.Println(t.A)
t1 := Test{
A: 4,
}
fmt.Println("demo2:")
fmt.Println(t1.A)
(&t1).SetA1(6)
fmt.Println(t1.A)
}
// output:
demo1:
3
3
demo2:
4
6
看上面的demo我們知道, 當(dāng)receiver不是指針時(shí)調(diào)用SetA其值根本沒有改變。
因?yàn)镚o中都是值傳遞,所以你如果對SetA的receiver的名稱命名為this,?self等,它就已經(jīng)失去了本身的意義——“調(diào)用一個(gè)對象的方法就是向該對象傳遞一條消息”。而且對象本身的屬性也并不一定會發(fā)生改變。
綜上: 請各位讀者在對receiver命名時(shí)不要再用this,?self等具有特殊含義的名稱啦。
Receiver是可以為nil的!!!
最近在研讀h2_bundle.go的時(shí)候,發(fā)現(xiàn)了一段特殊的代碼,頓時(shí)驚出一身冷汗,姑在本文補(bǔ)充一下,以防止自己和各位讀者踩坑。
源代碼截圖如下:?
驚出我一身冷汗的正是圖中標(biāo)紅的部分,receiver居然還要判斷為nil!在我的潛意識里一直是這樣認(rèn)為的,receiver默認(rèn)都是有值的,直接使用就行了。這簡直顛覆我的認(rèn)知,嚇得我趕緊寫了個(gè)demo驗(yàn)證一下:
type A struct {
v int
}
func (a *A) test() {
fmt.Println(a == nil)
}
func (a *A) testV() {
fmt.Println(a.v)
}
func main() {
var a *A
a.test()
a.testV()
}
上述輸出如下:

a.test()能夠正常輸出,只有在處理變量結(jié)構(gòu)體內(nèi)部變量v才報(bào)出panic!!!還好本文前面已經(jīng)介紹了Receiver是方法的第一個(gè)參數(shù)。正因?yàn)槭堑谝粋€(gè)參數(shù)所以僅僅作為參數(shù)傳遞時(shí)即使是nil也能夠正常調(diào)用函數(shù),而在真正使用的地方報(bào)出panic。
鑒于receiver如此特殊,所以特意在本文完成之后補(bǔ)充后續(xù)內(nèi)容以時(shí)刻提醒自己和各位讀者。
本部分于20200827日晚補(bǔ)充。
歡迎關(guān)注公眾號大家一同學(xué)習(xí)進(jìn)步:
最后, 祝各位事業(yè)有成!
-------------------?End?-------------------
往期精彩文章推薦:

歡迎大家點(diǎn)贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持
想加入Go學(xué)習(xí)群請?jiān)诤笈_回復(fù)【入群】
萬水千山總是情,點(diǎn)個(gè)【在看】行不
