Lesson 1 · 1 of 20
Interfaces and implicit implementation · Go
Define interfaces in Go and see how a type satisfies one just by having its methods, with no implements keyword: carriers and shipping costs.

An interface is a list of methods. Carrier asks for two: Name() string and Cost(kg float64) float64. It says nothing about how anything is worked out, only what you can ask a carrier for.
Courier and PostOffice are ordinary structs with methods, and neither of them says it implements Carrier: in Go, implementation is implicit. Having the methods with the exact signatures is enough. PostOffice.Cost charges the same whatever the weight, which is why its float64 parameter has no name.
The line var _ Carrier = PostOffice{} is a common trick: it assigns a PostOffice to a Carrier variable that is thrown away with _. If a method ever goes missing or changes its signature, the program does not compile, and the error points right there.
In main, carriers is a []Carrier holding both types. The loop calls c.Name() and c.Cost(kg) without knowing what is behind them: at run time the method of the concrete type is used. That is why the courier is cheaper for 1 and 2 kg (5.70 and 6.90) and the post office wins at 5 kg (7.00).
Adding a carrier does not touch the loop: all it takes is a new type with Name and Cost. Go code tends to keep interfaces small, one or two methods, and to define them in the package that uses them.
Code · main.go
package mainimport "fmt"// Any type with these two methods is a Carrier: there is no "implements".type Carrier interface { Name() string Cost(kg float64) float64}type Courier struct{ PerKg float64 }func (c Courier) Name() string { return "Courier" }func (c Courier) Cost(kg float64) float64 { return 4.5 + c.PerKg*kg }type PostOffice struct{ Flat float64 }func (p PostOffice) Name() string { return "Post office" }func (p PostOffice) Cost(float64) float64 { return p.Flat } // same price for any weightvar _ Carrier = PostOffice{} // compile-time check: fails if a method is missingfunc main() { carriers := []Carrier{Courier{PerKg: 1.2}, PostOffice{Flat: 7}} for _, kg := range []float64{1, 2, 5} { for _, c := range carriers { fmt.Printf("%3.0f kg %-12s %6.2f EUR\n", kg, c.Name(), c.Cost(kg)) } }}Output · go run main.go
1 kg Courier 5.70 EUR 1 kg Post office 7.00 EUR 2 kg Courier 6.90 EUR 2 kg Post office 7.00 EUR 5 kg Courier 10.50 EUR 5 kg Post office 7.00 EUR
Key points
- An interface is a set of methods.
- A type satisfies an interface by having its methods; there is no
implements. - A
[]Carriercan hold values of different types. var _ Carrier = PostOffice{}checks at compile time that the type fits.- Prefer small interfaces, defined where they are used.
Exercise
Create a Locker type (a parcel locker) with Name() and a flat Cost of 3.95, add it to carriers and check that the loop uses it without any other change.