Golang Interfaces

By Xavi

Interfaces

Like in many other object-oriented programming languages, interfaces in Go provide an umbrella that gathers together all specific types or objects implementing it. This generalization is helpful and convenient when more than one type can perform the same action (with subtle differences) or when these different types need to be grouped based on their behavior.

However, unlike other languages, Go’s interfaces are satisfied implicitly, that is, there’s no explicit connection between an interface and the types implementing it. A concrete type satisfies an interface (abstract type) just by implementing the method or methods indicated by it. This allows to treat totally unrelated types as the same just by their minimum overlapping features, creating a very decoupled relationship system and cutting down much of the bloated code compared to other languages.

Let’s see an example:

	type car struct{ speed float32 }

	func (c *car) accelerate() { c.speed += 10.0 }
	func (c *car) decelerate() { c.speed -= 10.0 }
	func (c *car) stop()       { c.speed = 0.0 }

	type motorbike struct {
		speed        float32
		feetOnGround bool
	}

	func (m *motorbike) accelerate() {
		m.speed += 20.0
		m.feetOnGround = false
	}
	func (m *motorbike) decelerate() {
		m.speed -= 20.0
		if m.speed <= 0.0 {
			m.speed = 0.0
			m.feetOnGround = true
		}
	}
	func (m *motorbike) stop() {
		m.speed = 0.0
		m.feetOnGround = true
	}

	type airplane struct {
		speed  float32
		onLand bool
	}

	func (a *airplane) accelerate() { a.speed += 100.0 }
	func (a *airplane) decelerate() { a.speed -= 100.0 }
	func (a *airplane) land() {
		a.onLand = true
	}
	func (a *airplane) takeOff() {
		a.onLand = false
	}

	type vehicle interface {
		accelerate()
		decelerate()
	}

Although car, motorbike and plane don’t share the same methods and the implementation details for the ones they share are different among them, the interface vehicle is the glue that tights them together. As it can be seen, there’s no explicit relationship between each of the concrete types and vehicle, just the implementation of the methods indicated by it.

This decoupling eases the creation of new groups even for already created concrete types:

	type aircraft interface {
		land()
		takeOff()
	}

In this case car, motorbike and airplane can be considered instances of vehicle, but only airplane is an instance of aircraft.

An instance of an interface, or interface value, is composed by two elements: a concrete type and a value of that type. These are known as its dynamic type and dynamic value.

When an interface variable is declared, both dynamic type and dynamic value are set to nil, this is the interface’s zero value. An interface will be nil if it’s dynamic type is nil and it can be checked using anInterface == nil or anInterface != nil. A very important thing to note is that calling any method on a nil interface will cause a panic. And also as important is to pay attention to a gotcha using interfaces: an interface whose dynamic type is a pointer which happens to be nil is not nil. Although it can be easily reasoned about as the interface having its dynamic type being a pointer and its dynamic value being nil, that’s a distinction that can very well slip through.

Let’s see some examples:

	var v vehicle    // v's dynamic type and dynamic value are nil
	v == nil         // true
	v.accelerate()   // panic: runtime error: invalid memory address or nil pointer dereference (interface is nil)
	v = &motorbike{} // v's dynamic type is *motorbike and its dynamic value is an instance of pointer to motorbike
	v.accelerate()   // motorbike's accelerate method is executed
	var c *car       // pointer to car declaration, pointer value is nil as per its zero value
	v = c            // 
	v == nil         // false; Although v's dynamic value is nil, its dynamic type is pointer to car
	v.accelerate()   // panic: runtime error: invalid memory address or nil pointer dereference
					 // v's accelerate() method is executed but v.speed is uninitialized

When a variable is assigned to an interface value or declared as one, it only exposes what it can do through the interface’s methods:

	var v vehicle   // v's dynamic type and dynamic value are nil
	v = &airplane{} // v's dynamic type is *airplane and its dynamic value is an instance of pointer to airplane
	v.takeOff()     // v.takeOff undefined (type vehicle has no field or method takeOff)

To be able to extract all the range of potential methods a type assertion needs to be performed to such interface value. A type assertion is expressed like i.(T) where i is an interface value and T is the asserted type, that is, the type to be obtained and which the dynamic type of i is compared against.

	var v vehicle           // v's dynamic type and dynamic value are nil
	v = &airplane{}         // v's dynamic type is *airplane and its dynamic value is an instance of pointer to airplane
	a, ok := v.(&airplane)  // type assertion from vehicle to airplane
	a.takeOff()             // now a.takeOff() is safely ran

When running a type assertion there are two options, either the asserted type is a concrete type (airplane like in the example above) or it is an abstract type (interface, like for example aircraft). In any case, if the interface value the assertion is applied to is nil the operation panics.

In the first case, the type assertion checks if T is exactly the same type as i’s dynamic type. If types match, the type assertion extracts i’s dynamic value. Alternatively, if types don’t match the type assertion fails, and panics unless a second variable is used to contain operation’s success or failure status:

	var v vehicle           // v's dynamic type and dynamic value are nil
	v = &car{}              // v's dynamic type is *car and its dynamic value is an instance of pointer to car
	a := v.(*airplane)      // panic: interface conversion: vehicle is *car, not *airplane
	a, ok := v.(*airplane)  // a is v's zero value and ok is false; doesn't cause a panic

In the second case, where T is an abstract or interface type, the type assertion checks whether i’s dynamic type satisfies T. The result of a successful assertion is like an interface casting where assertion’s target keeps the same but the resulting variable satisfies T:

	var v vehicle           // v's dynamic type and dynamic value are nil
	v = &airplane{}         // v's dynamic type is *airplane and its dynamic value is an instance of pointer to airplane
	v.land()                // v.land undefined (type vehicle has no field or method land)
	a, ok := v.(aircraft)   // a's dynamic type is *airplane and a's dynamic value is v's dynamic value
	a.land()                // now a.land() is safely ran

Comparing Interfaces

Interface values are comparable, that is, they can be applied the equality operators == and !=. For example:

	var x interface{} = 3    // x's dynamic type is int and dynamic value is immediate value 3
	x == 3                   // true

However, there’s a dangerous hidden pitfall when interfaces hold non-comparable types, like slices for instance. From the docs:

Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil. A comparison of two interface values with identical dynamic types causes a run-time panic if values of that type are not comparable. This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.

Let’s see a code example of the above:

	var slice1 interface{} = []int{1, 2}                   // dynamic type is slice of int (not comparable)
	var slice2 interface{} = []int{1, 2}                   //
	slice1 == slice2                                       // panic: runtime error: comparing uncomparable type []int

	var arrayOfSlice1 interface{} = [1][]int{[]int{1, 2}}  // dynamic type is array[1] of slice of int
	var arrayOfSlice2 interface{} = [1][]int{[]int{1, 2}}  //
	arrayOfSlice1 == arrayOfSlice2                         // panic: runtime error: comparing uncomparable type [1][]int


	type structWithSliceField struct {
		myInterface interface{}
	}

	var struct1 interface{} = structWithSliceField{myInterface: []int{1, 2, 3}}
	var struct2 interface{} = structWithSliceField{myInterface: []int{1, 2, 3}}
	struct1 == struct2                  // panic: runtime error: comparing uncomparable type []int

So not only non-comparable types, but other constructs containing them that otherwise are comparable are subject to this pitfall. When comparing two interface values extra attention must be put to avoid sneaky runtime panics.

Type switches

Interfaces can be used to to treat different types in a uniform way based on their behavior, but there’s a construct in Go that also facilitates the execution of specific code based on interfaces, the type switch.

Like ad-hoc polymorphism, referred to as method or/and operator overloading in other object-oriented programming languages, the type switch executes, for a uniform call, a specific branch of code based on the type of the argument.

For instance, the following function returns a string based on the dynamic type of v:

	func entity(v vehicle) string {
		switch v := v.(type) {
		case nil:
			return "I'm nothing"
		case *car:
			return "I'm a car"
		case *motorbike:
			return "I'm a motorbike"
		case *airplane:
			return "I'm an airplane"
		default:
			return fmt.Sprintf("I'm  an instance of %T", v)
		}
	}

Although entity() accepts a vehicle as argument, only the code relevant to the vehicle’s specific dynamic type gets executed. It is said in Go terms then that types members of the switch form a discriminated union. The expression switch v := v.(type) can be simplified to switch v.(type) if v is not needed in any of the switch cases. If using the former, it is common to use the same variable name as the argument shadowing the interface with it’s dynamic type for any given branch. As cases can contain other interfaces, it is important to pay extra attention to the order of cases. If two or more types are to be grouped in a case, the type of v within the branch is v’s original type, in such case there’s no value extraction.

Function entity() at work looks like below:

	var v vehicle           //
	fmt.Println(entity(v))  // I'm nothing
	v = &car{}              //
	fmt.Println(entity(v))  // I'm a car
	v = &motorbike{}        //
	fmt.Println(entity(v))  // I'm a motorbike
	v = &airplane{}         //
	fmt.Println(entity(v))  // I'm an airplane