Implementing an interface

May 9, 2023

It feels like it’s been a month since we started on interfaces. Today we’re covering the final section on this topic!

Implementing an interface

A type T implements an interface I if

  • T is not an interface and is an element of the type set of I; or
  • T is an interface and the type set of T is a subset of the type set of I.

A value of type T implements an interface if T implements the interface.

All three of these points should be pretty intuitive, especially if you’ve read the rest of the interface posts, but let’s give an example of each just to be sure.

  1. T is not an interface and is an element of the type set of I

    type Float interface {
    	~float32 | ~float64
    }
    
    type myFloat float64 // myFloat implements Float; it is not
                         // an interface, and it is an element
                         // of the type set of Float.
    
    type Fooer interface {
    	Foo()
    }
    
    type myFooer struct {} // myFooer implements Fooer; it is not
                           // an interface, and it is an element
                           // of the type set of Fooer by virtue of
                           // the fact that it implements the Foo()
                           // method
    
    func (f *myFooer) Foo() {}
    
  2. T is an interface and the type set of T is a subset of the typset of I

    type FooBarer interface {
    	Foo()
    	Bar()
    }
    
    var fb FooBarer
    var f Fooer = fb // FooBarer implements Fooer, because
                     // it is an interface, and has a type set
                     // that is a subset of FooBarer's type set.
    
  3. A value of type T implements an interface if T implements the interface.

    This just extends the type implementation rules to values of that type.

Quotes from The Go Programming Language Specification Version of December 15, 2022

Share this

Related Content

Operator constraints

Last week I inconspicuously skipped over one sentence in the spec, the last sentence in the section on Operands, and jumped straight ahead to the section on Qualified identifiers. This is because the sentence didn’t make immediate sense to me, and I needed time to research it. I’ve done that research now, so, let’s jump back and cover the missed territory. Operands … Implementation restriction: A compiler need not report an error if an operand’s type is a type parameter with an empty type set.

Unions of type sets

General interfaces … Union elements denote unions of type sets: // The Float interface represents all floating-point types // (including any named types whose underlying types are // either float32 or float64). type Float interface { ~float32 | ~float64 } Here we see our first example of a union of type sets. The example shows an interface of all types with an underlying float type. The equivalent for integer types is a bit longer, due to the large number of distinct integer types in Go, but would look like:

Type parameters in method definitions

Yesterday we saw that when a method is defined on a generic type, the receiver must include type parameters. Now for all the relevant details: Method declarations … … Syntactically, this type parameter declaration looks like an instantiation of the receiver base type: the type arguments must be identifiers denoting the type parameters being declared, one for each type parameter of the receiver base type. The type parameter names do not need to match their corresponding parameter names in the receiver base type definition, and all non-blank parameter names must be unique in the receiver parameter section and the method signature.