Interface — defines a set of method signatures (behavior contract). Struct — defines named, typed fields (data layout).
Interfaces enable polymorphism through implicit satisfaction. Any type with the required methods automatically implements the interface — no keyword needed. This makes it easy to swap implementations and write tests.
1type Transport interface {2 Send(data []byte) error3 Receive() ([]byte, error)4}56type HTTPTransport struct{ BaseURL string }7func (h *HTTPTransport) Send(data []byte) error {8 _, err := http.Post(h.BaseURL, "application/json", bytes.NewReader(data))9 return err10}11func (h *HTTPTransport) Receive() ([]byte, error) {12 resp, err := http.Get(h.BaseURL)13 if err != nil { return nil, err }14 return io.ReadAll(resp.Body)15}
Structs hold the data and implement interfaces. You can embed structs for composition. Structs are concrete — the compiler knows their exact fields and types.
1type Config struct {2 Host string3 Port int4}5type Server struct {6 Config // embed for composition7 Name string8}
When to use:
Common mistakes:
any — loses type safety.