Interfaces
What you'll learn:
- explain what an interface is and how a type implements one without the word
implements; - change how
fmtprints your type with aStringmethod; - store a value of any type in
anyand get it back out with a type assertion; - pick an action by a value's type with a type switch.

Picture a job ad for a courier. It does not say who is wanted: a student, a retiree or a former taxi driver. It says something else: "can drive a car and knows the city". Anyone who can do those two things fits. The employer does not care what the candidate did before—only what they can do.
Programs run into a similar situation. Different types can have methods with the same name, parameters and result. The rectangle and the square below both have an Area method that returns the shape's area:
package main
import "fmt"
type Rect struct{ Width, Height float64 }
type Square struct{ Side float64 }
func (r Rect) Area() float64 { return r.Width * r.Height }
func (s Square) Area() float64 { return s.Side * s.Side }
func main() {
r := Rect{Width: 2, Height: 3}
fmt.Println(r.Area(), Square{Side: 2}.Area()) // 6 4
}Where is the problem? A function that needs an area has to name the type of its parameter. The function func printArea(r Rect) accepts only a Rect, and a call with a Square does not compile. Writing a separate copy of the function for each shape is awkward: the copies have identical bodies, and only the parameter type differs. You need a way to say "any shape that can compute its area will do".
Declaring an interface
An interface is a type that lists methods: their names, parameters and results. These methods have no bodies. This is the job ad: a list of what a value must be able to do. The declaration type Shape interface { … } has these parts:
type Shape—a new type namedShape;interface—the kind of type: a list of methods;Area() float64inside the braces—a required method: name, parameters and result, with no body.
The Shape interface requires one method, and the function printArea accepts any value of type Shape:
type Shape interface {
Area() float64
}
func printArea(s Shape) {
fmt.Println(s.Area())
}The parameter s of printArea has the type Shape. Inside the function, one thing is known about s: it has an Area method that can be called. Which type stands behind s, the function does not know, and it does not need to.
No implements keyword
A type implements an interface if it has all of the interface's methods with the same parameters and results. People also say the type satisfies the interface. You don't declare this: Go has no implements keyword.
Here the job-ad picture ends. In real life a candidate comes and applies, but in Go a type declares nothing and does not even know about Shape. The compiler matches the methods, and it is strict: an Area method that returns int instead of float64 does not fit.
The compiler checks the match in two places:
- when a function with a parameter of an interface type is called;
- when a value is assigned to a variable of an interface type.
If the type lacks a required method, or the method's result has a different type, the program does not compile.
Different types, one interface
Rect and Square both have an Area() float64 method, so both implement Shape. The function printArea accepts either of them, and a slice []Shape can hold them side by side:
printArea(Rect{Width: 2, Height: 3}) // 6
printArea(Square{Side: 2}) // 4
shapes := []Shape{Square{Side: 1}, Rect{Width: 1, Height: 5}}
fmt.Println(len(shapes), shapes[1].Area()) // 2 5Let's walk through the example line by line:
- The first call passes a rectangle to
printAreaand prints6. - The second passes a square and prints
4. - The slice
shapesholds two shapes of different types. Each element holds a value of its own concrete type. len(shapes)gives2, andshapes[1].Area()runs theAreamethod of the type stored inside—here aRectwith area5.
Pointer receivers and interfaces
Interviewers like to ask this: does Rect implement Shape if the method is declared as func (r *Rect) Area() float64? The answer depends on the kind of receiver.
A method with a pointer receiver
No, it does not. A method with a pointer receiver belongs to the type *Rect, and Rect does not have it. So var s Shape = Rect{Width: 2, Height: 3} does not compile: Rect does not implement Shape (method Area has pointer receiver). An address works: var s Shape = &Rect{Width: 2, Height: 3}.
Calling r.Area() on a variable r still works: Go takes its address for you, as you saw in the previous theme. But an interface needs a type that really has the method.
A method with a value receiver
The reverse rule is gentler: a method with a value receiver belongs to both Rect and *Rect. So with the methods from our example, both types implement Shape.
Common mistake
A struct field was accessed through a variable of an interface type:
var s Shape = Rect{Width: 2, Height: 3}
fmt.Println(s.Width) // ✗s.Width undefined (type Shape has no field or method Width)The program does not compile: an interface lists only methods, and it has no fields. Even though s holds a Rect with a Width field, only what Shape declares is available through s—the Area method. Through the job ad you see only what the ad says.
Key points
- An interface lists methods; a type implements it if it has all of them with the same parameters and results.
- Go has no
implementskeyword: the compiler checks the match. - A method with a
*Rectreceiver belongs only to*Rect, so*Rectimplements the interface andRectdoes not. - Through a variable of an interface type you reach only the interface's methods, not fields.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.