Pointers vs. Values
正如ByteSize
那样,我们可以为任何已命名的类型(除了指针或接口)定义方法; 接收者可不必为结构体。
在之前讨论切片时,我们编写了一个Append
函数。 我们也可将其定义为切片的方法。为此,我们首先要声明一个已命名的类型来绑定该方法, 然后使该方法的接收者成为该类型的值。
typeByteSlice[]bytefunc(sliceByteSlice)Append(data[]byte)[]byte{//BodyexactlythesameastheAppendfunctiondefinedabove.}
我们可以通过指针的方式作为ByteSlice
的接收者。这样就可以重写
func(p*ByteSlice)Append(data[]byte){slice:=*p//Bodyasabove,withoutthereturn.*p=slice}
示例
typeByteSlice[]bytefunc(sliceByteSlice)Append(data[]byte)[]byte{//BodyexactlythesameastheAppendfunctiondefinedabove.byteSlice:=append(slice,data...)returnbyteSlice}func(p*ByteSlice)Append2(data[]byte){byteSlice:=append(*p,data...)*p=byteSlice}funcmain(){slice:=ByteSlice{}byteSlice:=append(slice,byte(1))bytes:=byteSlice.Append([]byte{byte(2)})fmt.Println(bytes)//[12]byteSlice.Append2([]byte{byte(3)})fmt.Println(byteSlice)//[31]}
事实上我们可以做到更好。如我们修改方法跟write
接口一样。
func(p*ByteSlice)Write(data[]byte)(nint,errerror){slice:=*p//Againasabove.*p=slicereturnlen(data),nil}
//WriteristheinterfacethatwrapsthebasicWritemethod.////Writewriteslen(p)bytesfromptotheunderlyingdatastream.//Itreturnsthenumberofbyteswrittenfromp(0<=n<=len(p))//andanyerrorencounteredthatcausedthewritetostopearly.//Writemustreturnanon-nilerrorifitreturnsn<len(p).//Writemustnotmodifytheslicedata,eventemporarily.////Implementationsmustnotretainp.typeWriterinterface{Write(p[]byte)(nint,errerror)}
那么类型*ByteSlice
就满足了标准的io.Writer
接口,这将非常实用。 例如,我们可以通过打印将内容写入。
varbByteSlicefmt.Fprintf(&b,"Thishourhas%ddays\n",7)
我们将ByteSlice
的地址传入,因为只有*ByteSlice
才满足io.Writer
。以指针或值为接收者的区别在于:值方法可通过指针和值调用, 而指针方法只能通过指针来调用。
之所以会有这条规则是因为指针方法可以修改接收者;通过值调用它们会导致方法接收到该值的副本, 因此任何修改都将被丢弃,因此该语言不允许这种错误。
不过有个方便的例外:若该值是可寻址的, 那么该语言就会自动插入取址操作符来对付一般的通过值调用的指针方法。在我们的例子中,变量b是可寻址的,因此我们只需通过b.Write来调用它的Write方法,编译器会将它重写为(&b).Write。
顺便一提,在字节切片上使用Write
的想法已被bytes.Buffer
所实现。
//Writeappendsthecontentsofptothebuffer,growingthebufferas//needed.Thereturnvaluenisthelengthofp;errisalwaysnil.Ifthe//bufferbecomestoolarge,WritewillpanicwithErrTooLarge.func(b*Buffer)Write(p[]byte)(nint,errerror){b.lastRead=opInvalidm,ok:=b.tryGrowByReslice(len(p))if!ok{m=b.grow(len(p))}returncopy(b.buf[m:],p),nil}
引用
Effective Go - The Go Programming Language (google.cn)
方法 |《高效的 Go 编程 Effective Go 2020》| Go 技术论坛 (learnku.com)