Go Crash Course X

Gopher

Part 9

Interface

Interface is a collection of method signatures. In go interfaces are implemented implicitly. So it is possible for a single type to implement multiple interfaces. For example the os.File type implements dozens of interfaces from the io package alone like Reader, Writer, ReadCloser, ReadSeeker, Closer, WriteSeeker. Because it is implicit it can also implement newer interfaces from other packages and from packages you write.

Implementing Interface

Any user defined type can implement interfaces. Usually we see interfaces implementation on structs. But we can very easily do this on any other type definitions.

type bob int

func (b bob) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("hello world!"))
}

This bob type here now implements the handler interface and can be used to serve http request.

But usually we would be using a struct type to implement interface because we need additional fields in our methods.

Lets look at the classic shape interface

type Shape interface {
	Area() float64
}

So something is a Shape as long as it implements the methods listen in Shape interface.

type Circle struct {
	Radius float64
}

type Rectangle struct {
	Width  float64
	Height float64
}

func (c *Circle) Area() float64 {
	return math.Pi * c.Radius * c.Radius
}

func (r Rectangle) Area() float64 {
	return r.Height * r.Width
}

Now we have two struct Circle and Rectangle that has the Area method. Pay attention to the pointer receiver on type Circle’s Area method.

func GetArea(s Shape) float64 {
	return s.Area()
}

func main() {
	circle := Circle{5.0}
	r1 := Rectangle{4.0, 5.0}
	r2 := &Rectangle{10.0, 15.0}
	GetArea(circle) // this will give error
	GetArea(r1)
	GetArea(r2)
}

Say we have a function called GetArea that takes Shape. Then we can create a few variables of type Circle and Rectangle. circle is of type Circle, r1 is of type Rectangle and r2 is of type *Rectangle. Since Area method is is attached to value receiver of Rectangle both r1 and r2 can be used as Shape. But circle does not work as Shape. The exact error we get is

cannot use circle (type Circle) as type Shape in argument to GetArea:
        Circle does not implement Shape (Area method has pointer receiver)

Basically if a method has pointer receiver to satisfy the interface we need the pointer of the type. Which is a bit different to how structs work. We can call Area on value of circle

area := circle.Area() // this works fine although circle is a value

Type Cast

We can store any value in a variable of type interface

var x interface{} = "hello"

str, ok := x.(string)

We can create a variable x with value "hello". Since we know the value is of type string we can type cast that interface using the .(type) syntax. This returns the value and a bool which is set to false if the value could not be converted. Notice we can only do this because the underlying type was string. We can do similar things for structs as well.

Type Switches

var y interface{} = "go"
switch v := y.(type) {
case int:
	fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
	fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
	fmt.Printf("I don't know about type %T!\n", v)
}

The use cases for these are not as common. Usually in Go we try not to pass around interface{} unless we absolutely have to. We might see specific interface being passed to function. But in those cases we never have to type cast them to concrete types as we are more interested in the behavior rather than the underlying fields.

Next Steps

This is Part 10 of this Go crash course series.



comments powered by Disqus