## 指针 vs. 值 正如我们从`ByteSize`上看到的,任何命名类型(指针和接口除外)都可以定义方法(method);接收者(receiver)不必为一个结构体。 在上面有关切片的讨论中,我们编写了一个`Append`函数。我们还可以将其定义成切片的方法。为此,我们首先声明一个用于绑定该方法的命名类型,然后将方法的接收者作为该类型的值。 ~~~ type ByteSlice []byte func (slice ByteSlice) Append(data []byte) []byte { // Body exactly the same as above } ~~~ 这样还是需要方法返回更新后的切片。我们可以通过重新定义方法,接受一个`ByteSlice`的*指针*作为它的接收者,来消除这样笨拙的方式。这样,方法就可以改写调用者的切片。 ~~~ func (p *ByteSlice) Append(data []byte) { slice := *p // Body as above, without the return. *p = slice } ~~~ 实际上,我们可以做的更好。如果我们将函数修改成标准`Write`方法的样子,像这样, ~~~ func (p *ByteSlice) Write(data []byte) (n int, err error) { slice := *p // Again as above. *p = slice return len(data), nil } ~~~ 那么类型`*ByteSlice`就会满足标准接口`io.Writer`,这样就很方便。例如,我们可以打印到该类型的变量中。 ~~~ var b ByteSlice fmt.Fprintf(&b, "This hour has %d days\n", 7) ~~~ 我们传递`ByteSlice`的地址,是因为只有`*ByteSlice`才满足`io.Writer`。关于接收者对指针和值的规则是这样的,值方法可以在指针和值上进行调用,而指针方法只能在指针上调用。这是因为指针方法可以修改接收者;使用拷贝的值来调用它们,将会导致那些修改会被丢弃。 顺便说一下,在字节切片上使用`Write`的思想,是实现`bytes.Buffer`的核心。