Interfaces
No Pointers to Interfaces
You almost never need a pointer to an interface. You should be passing interfaces as values—the underlying data can still be a pointer.
An interface is two fields:
A pointer to some type-specific information. You can think of this as "type."
Data pointer. If the data stored is a pointer, it’s stored directly. If the data stored is a value, then a pointer to the value is stored.
If you want interface methods to modify the underlying data, you must use a pointer.
Verify Interface Compliance
Verify interface compliance at compile time where appropriate. This includes:
Exported types that are required to implement specific interfaces as part of their API contract
Exported or unexported types that are part of a collection of types implementing the same interface
Other cases where violating an interface would break users
The statement var _ http.Handler = (*Handler)(nil)
will fail to compile if *Handler
ever stops matching the http.Handler
interface.
The right hand side of the assignment should be the zero value of the asserted type. This is nil
for pointer types (like *Handler
), slices, and maps, and an empty struct for struct types.
Receivers and Interfaces
Methods with value receivers can be called on pointers as well as values.
Methods with pointer receivers can only be called on pointers or addressable values.
Similarly, an interface can be satisfied by a pointer, even if the method has a value receiver.
Avoid Embedding Types in Public Structs
These embedded types leak implementation details, inhibit type evolution, and obscure documentation.
Assuming you have implemented a variety of list types using a shared AbstractList
, avoid embedding the AbstractList
in your concrete list implementations. Instead, hand-write only the methods to your concrete list that will delegate to the abstract list.
Go allows type embedding as a compromise between inheritance and composition. The outer type gets implicit copies of the embedded type's methods. These methods, by default, delegate to the same method of the embedded instance.
The struct also gains a field by the same name as the type. So, if the embedded type is public, the field is public. To maintain backward compatibility, every future version of the outer type must keep the embedded type.
An embedded type is rarely necessary. It is a convenience that helps you avoid writing tedious delegate methods.
Even embedding a compatible AbstractList interface, instead of the struct, would offer the developer more flexibility to change in the future, but still leak the detail that the concrete lists use an abstract implementation.
Last updated