This is the 23rd day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
The value of a pointer vs
As we saw in ByteSize, methods can be defined for any type (except Pointers or interfaces); The receiver need not be a structure.
In the previous discussion of slicing, we wrote an Append function. We can define it as a method on a slice. To do this, we first declare a named type to which the method can be bound, and then make the receiver of the method the value of that type.
type ByteSlice []byte
func (slice ByteSlice) Append(data []byte) []byte {
// Body exactly the same as the Append function defined above.
}
Copy the code
This still requires returning the method to update the slice. We can eliminate this clumsiness by redefining the method to have a pointer to ByteSlice as its receiver, so that the method overwrites the caller’s slice.
func (p *ByteSlice) Append(data []byte) {
slice := *p
// Body as above, without the return.
*p = slice
}
Copy the code
In fact, we can do better. If we modify our function to look like a standard Write method, like this,
func (p *ByteSlice) Write(data []byte) (n int, err error) {
slice := *p
// Again as above.
*p = slice
return len(data), nil
}
Copy the code
This way the *ByteSlice type implements the standard interface IO.Writer, which would be handy. For example, we could print it like this.
var b ByteSlice
fmt.Fprintf(&b, "This hour has %d days\n", 7)
Copy the code
We pass a ByteSlice address because only *ByteSlice satisfies io.writer. One of the differences or choices about whether the receiver is a pointer or a value is that value methods can be called on Pointers and values, but pointer methods can only be called on Pointers.
This rule occurs because pointer methods can modify receivers; Calling them on a value causes the method to receive a copy of that value, so any changes are actually changes to the copy, not to the original value. Therefore, the Go language does not allow such errors. There is, however, one exception. When the value is addressable, the language handles calling pointer methods on the value by automatically inserting the address operator. In our case, variable B is addressable, so we can call its Write method using only B.rite. The compiler will rewrite it to (&b).write.
By the way, implementing the Write method on byte slices is at the heart of implementing bytes.buffer.