Go Interfaces: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(277 intermediate revisions by the same user not shown)
Line 1: Line 1:
=External=
* https://go.dev/ref/spec#Interface_types
=Internal=
=Internal=
 
* [[Go Language]]
* [[Go Concepts - The Type System#User-Defined_Types|The Type System]]
* [[Go_Language_Object_Oriented_Programming#Interfaces|Object-Oriented Programming in Go]]
* [[Go_Methods#Overview|Methods]]


=Overview=
=Overview=


Go has an unique interface system that allows modeling behavior rather than modeling types. An interface declares the behavior of a type. The availability of interfaces in the language allows for [[Programming#Polymorphism|polymorphism]].
An '''interface''' is a set of method signatures. The interface is declared with the keyword <code>[[Go_Language#type_keyword|type]]</code>, followed by the name of the interface, followed by the keyword <code>[[Go_Language#interface_keyword|interface]]</code> and by the method signatures enclosed in a curly brace block. Each signature contains the method name, optional parameter names and mandatory parameter types, and optional return variable names and mandatory return types. Providing parameter and return names is recommended, as it arguably exposes more semantics, making the code clearer. See [[#Declaration|example]], below.  


<font color=red>Types may implement interfaces. Pointer types may implement interfaces. These cases are not always identical.</font>
The interface declaration contains no method implementations.  


There is no need to declare that an interface is implemented, the compiler does the work of determining whether the values of a type satisfy an interface. This is called [[Programming#Duck_Typing|duck typing]]. There is no "implements" or "extends" keyword in Go. ''Is this a good thing? As far as I can tell, I can't say whether a specific type implements an interface, short of browsing all methods in the package, looking for receivers of that specific type. This does not help code readability.'' So, if a type implements the methods of an interface, a value of the type can be stored in a value of that interface type and the compiler ensure the assignments are correct at compile-time.
Interfaces are not fully-featured types, they're less than that, but they're used to express conceptual similarities between types. If several types implement the same interface, they are similar in a way that is important to the application. An interface highlights the similarities between the types implementing it and conceals the differences.


If a method call is made against an interface value, the equivalent method of the stored user-defined value - whatever that happens to be at the moment - it is executed. This is an expression of the polymorphic nature of the language. The user-defined type is often called a ''concrete type''.
<span id='accept_interfaces_return_structs'></span>An interface does need to be formally declared on a target type for that type to implement it. Interfaces are satisfied implicitly.  A type '''implements''' or '''satisfies''' an interface if the type defines '''all''' methods specified in the interface. Of course, the type can have other methods not specified by the interface. This is called <span id='Duck_Typing'></span>[[Programming_Languages_Concepts#Duck_Typing|duck typing]]. If a type implements an interface by the virtue of duck typing, a value of the type can be stored in a value of that interface type and the compiler ensure the assignments are correct at compile-time. A type can implement multiple interfaces.


Interfaces in Go tend to be small, exposing only a few functions, or just one function.
Interfaces can be thought of a kind of conceptual inheritance: types inherit behavior specified by an interface. Interfaces help supporting polymorphism in Go: different types implement different behaviors specified by the '''same''' interface. For more details see: {{Internal|Go_Inheritance_and_Polymorphism#Overview|Go Inheritance and Polymorphism}}


<font color=red>
Go interfaces allow programs to model behavior rather than model types. The behavior is "attached" to existing types, via methods, and independently, the behavior is given a name, the name of the interface that groups together the methods. Go type system is structural, not nominal. The interfaces are behavior contracts, and they belong in the consuming code, not in the producing code. '''A common pattern should be to accept interfaces and return structs'''.
In order to understand the mechanics of how interfaces work and whether a type implements an interface or not, you need to understand [[Go_Concepts_-_The_Type_System#Pointer_Types|pointer types]] and their relationship to their base types, and method sets:
</font>


<font color=red>
:::<font color=darkkhaki>This is debatable: for an interface itself, it feels natural that the methods should return the same interface, not an implementing type. What if the implementing type is package private, and the interface is public?</font>
'''TODO'''


<font color=darkkhaki>A type and its associated [[Go_Concepts_-_The_Type_System#Pointer_Types|pointer type]] may or may not implement the same interface, depending on how the methods are associated with the value and pointer receivers of that type.</font>


* Can only structs be interfaces, or there are other things that can be interfaces? "Go in Action" mentions ''named types''.
"The bigger the interface, the weaker the abstraction." The most important thing about interfaces in Go is that the smaller the interface is, the more useful it is. This is a Go-specific idea: we want to make little interfaces, so we can build components that share them.
* It seems that the values and pointers are interchangeable when used as interface - we can pass a value or a pointer when we invoke an interface method and the compiler will be fine.
* [[Go_Concepts_-_Functions#Receivers_and_Interfaces]]
* [[Go_Concepts_-_Functions#Receiver_Best_Practice]]
</font>


=Method Sets=
Interfaces are defined at the "fault lines" of the system's architecture. These are between the <code>main()</code> function and the rest, and along the package API boundaries.


A type may have a ''method set'' associated with it.  
Interfaces should not be defined before a realistic example of usage exists.


There are two kinds of method sets: [[#Interface_Method_Set|interface method sets]] and [[#User-Defined_Type_Method_Set|user-defined types method sets]].  
Interfaces are rigid. For a library that exposes interfaces in the public API, any change to the interface requires releasing a new major version, since it breaks all third-party implementations.
:::<font color=darkkhaki>Is that right? What if we '''add''' to the interface?</font>


If a type does not have any of the previously mentioned method sets, it is said to have an empty method set.
=Declaration=


In a method set, each method must have a unique non-blank method name.
The following syntax declares an interface:


The language specification defines the method sets here: https://golang.org/ref/spec#Method_sets.
<font size=-1.5>
<font color=green><b>type</b></font> <I><InterfaceName></I> <font color=green><b>interface</b></font> {
  <font color=teal>// Interface method set:</font>
  <I><method_signature_1></I>
  <I><method_signature_2></I>
  ...
}
</font>
Example:
<syntaxhighlight lang='go'>
type SomeInterface interface {
  MethodA(int) (int, error)                          // No parameter and return value names are specified.
  MethodB(someValue int) (someResult int, err error) // Parameter and return value names are provided.
                                                    // This is arguably clearer, as it conveys more semantics.
}
</syntaxhighlight>


What results after such a declaration is an <span id='Interface_Type'></span>'''interface type'''. For a discussion of concrete type vs. interface types, see: {{Internal|Go_Language#Concrete_vs._Interface_Types|Go Language &#124; Concrete vs. Interface Types}}
==Interface Method Set==
==Interface Method Set==
An interface method set the list of method declaration specified in the [[#Declaration|interface type declaration]].  The interface type declaration has the only purpose of defining the method set and giving it a name, which is the interface name. Also see: {{Internal|Go_Methods#Method_Set|Method Sets}}
=<span id='Idiomatic_Interface_Conventions'></span>Naming=
Single-method interfaces are named using the method name plus an "er" suffix. An interface with just one <code>Write</code> method would be called <code>Writer</code>. Sometimes, the result isn't correct English, but we do it anyway. In other cases, we use English to make the result nicer:
<syntaxhighlight lang='go'>
type ByteReader interface {
    ReadByte() (c byte, err error)
}
</syntaxhighlight>
When an interface includes multiple methods, choose a name that accurately describes its purpose.


An ''interface method set'' is a list of method declarations (function name and signature), specified in the [[#Interface_Type|interface type declaration]]. The interface type declaration has the only purpose of defining the method set and giving it a name - the interface name.
Also see: {{Internal|Go_Style#Naming|Go Style}}


The method set only contains method names and signatures, so the interface type does not define ''how'' the behavior is implemented, but just the behavior's contract. The interfaces allow us to hide the incidental details of the implementation. The behavior implementation details are contained by user-defined types, via methods.
=Implementing an Interface=
As mentioned [[#Overview|above]], interfaces are not formally declared to be satisfied with dedicated syntax, they are satisfied implicitly. The type satisfying an interface do not need to formally declare the name of the interface anywhere in its definition. Instead, it is sufficient to declare themselves as [[Go_Language_Object_Oriented_Programming#Value_Receiver_Type|value]] or [[Go_Language_Object_Oriented_Programming#Pointer_Receiver_Type|pointer receiver types]] for '''all''' the methods in the interface declaration.
<syntaxhighlight lang='go'>
type SomeImplementation { ... } // SomeInterface is specified nowhere in the SomeImplementation declaration


Also, the method signatures from the interface method set are independent on whether the corresponding methods from the concrete types are declared with value or pointer receivers.
// Declaring the type as pointer receiver for *all* methods declared in the interface makes
// the type to implement that interface . Value and pointer receivers must not be mixed by
// the method implementations of an interface.
func (s *SomeImplementation) MethodA(i int) (int, error) { ... }
func (s *SomeImplementation) MethodB(someValue int) (int, error) { ... }
</syntaxhighlight>
The method signatures from the interface method set are independent on whether the corresponding methods from the concrete types are declared with value or pointer receivers.
==When Does a Type/Pointer Type Implement an Interface?==


===Interface Type===
Note that notion of a type implementing an interface and its corresponding pointer type implementing the same interface both make sense, and they are subtly different. According to the language specification, a user-defined type <code>T</code> implements an interface if its [[Go_Language_Object_Oriented_Programming#Type_Method_Set|method set]], which are all methods declared with '''a value receiver''' of type <code>T</code>, is a superset of the interface. By extrapolation, a pointer type implements an interface when the method set associated with pointers to that type is a superset of the [[#Interface|interface]]. As explained [[Go_Language_Object_Oriented_Programming#Pointer_Type_Method_Set|here]], the method set associated with the pointer of a type includes implicitly the method set associated with the value of the type.
==What Can Implement an Interface?==


An ''interface type'' is a user-defined type whose only purpose is to introduce a method set (the [[#Interface_Method_Set|interface method set]]) and to give it a name.
Structs can implement interfaces.  


The interface type declaration starts with the <tt>type</tt> keyword, to indicated that this is a user-defined type, followed by the interface name and the keyword <tt>interface</tt>, followed by the interface method set declaration between curly braces. The interface method set declaration consists in a list of method names followed by their [[Go_Concepts_-_Functions#Function_Signature|signatures]]. Unlike in the <tt>struct</tt>'s case, we don't define fields.
Type aliases can implement interfaces. In general, [[Go_Language#Named_Type|named types]] can implement interfaces, like in the following example where a <code>bool</code> can implement an interface as long as it is named:


<pre>
<syntaxhighlight lang='go'>
type MyInterface interface {
type SomeInterface interface {
     SomeMethod()
    <function-name-1><function-signature-1>
     <function-name-2><function-signature-2>
    ...
}
}
</pre>
Example:


<pre>
type someImpl bool
type A interface {
 
    m1(i int) int
    m2(s string) string


func (s someImpl) SomeMethod() {
fmt.Printf("SomeMethod\n")
}
}
</pre>


===Interface Name Convention===
someImpl(true).SomeMethod()
</syntaxhighlight>


If the interface type contains only one method, the name of the interface starts with name of the method and ends with the ''er'' suffix.
<font color=darkkhaki>What else? Can a function be used as an interface?</font>


When multiple methods are declared within an interface type, the name of the interface should relate to its general behavior.
=Interface Values=
Declaring a variable of an interface type creates an '''interface value''':
<syntaxhighlight lang='go'>
var i SomeInterface // i is a pair: (dynamic_type, dynamic_value)
</syntaxhighlight>
The interface value contains a data pair, including a <span id='Dynamic_Type'></span>'''dynamic type''' and a <span id='Dynamic_Value'></span>'''dynamic value'''. The dynamic type, when set, is a [[Go_Language#Concrete_Type|concrete type]] that implements that interface. The dynamic value, when set, is an instance of the dynamic type. Internally, an interface variable is a two-word data structure. The first word contains a pointer to an internal table called <code>iTable</code>, which contains type information about the stored value: the concrete type of the value that has been stored and a list of methods associated with the value. The second word is a reference to the stored value. The combination of type information and pointer binds the relationship between the two values.


===Interface===
In the case of the above declaration, both elements of the pair are <code>nil</code>:
<syntaxhighlight lang='go'>
fmt.Printf("%v\n", i) // prints <nil>
</syntaxhighlight>
Assuming the following interface and concrete type declarations:
<syntaxhighlight lang='go'>
type SomeInterface interface {
  SomeMethod()
}


The method set of an interface type it is said to be the type's ''interface''.
type SomeImplementation struct {
  data string
}


==User-Defined Type Method Set==
// SomeMethod makes SomeImplementation implement SomeInterface.
 
func (s *SomeImplementation) SomeMethod() {
===Method Set associated with the Values of a Type===
  if s == nil {
 
    fmt.Println("invocation with a nil dynamic value")
The method set associated with the values of a type consists of all methods declared with a ''value receiver'' of that type.
  } else {
    fmt.Printf("data: %s\n", s.data)   
  }
}
</syntaxhighlight>
... the following variable declaration and initialization ends up in an interface variable whose both dynamic type and dynamic value are set to <code>*SomeImplementation</code> and <code>&SomeImplementation{"test"}</code>, respectively:
<syntaxhighlight lang='go'>
var i SomeInterface
i = &SomeImplementation{"test"}
</syntaxhighlight>
The interface value can be then used to invoke the <code>SomeMethod()</code> method on the dynamic value, which was assigned to the dynamic concrete type instance pointer.
<syntaxhighlight lang='go'>
i.SomeMethod() // prints "data: test"
</syntaxhighlight>


The specification defines ''the method set of a type'' as the method set associated with the values of the type.
An interface value can have a <code>nil</code> dynamic value, and still be usable, as long it is assigned a dynamic type with this syntax:
<syntaxhighlight lang='go'>
var i SomeInterface
var t *SomeImplementation
i = t
</syntaxhighlight>
An interface value assigned with a pointer to the dynamic type, but not with a dynamic value can still be invoked into, given that the method implementation cases for the situation when the receiver type [[Go_Language_Object_Oriented_Programming#Implicit_Argument|implicit argument]] is <code>nil</code>. In the example above, we assigned <code>i</code> a dynamic type <code>*SomeImplementation</code> but not a dynamic value, as <code>t</code> has no concrete value yet. The dynamic type is enough information to go find which implementation <code>SomeMethod()</code> should use.  


===Method Set associated with the Pointers of a Type===
{{Note|This is why it is a good idea to guard against <code>nil</code> implicit argument in all concrete methods provided by types that implement interfaces.}}


The method set associated with the pointers of a type <tt>T</tt> consists of both all methods declared with a pointer receiver <tt>*T</tt> ''and'' with a value receiver <tt>T</tt>.  
This way we get a static method, in the Java or Python sense of the word and an instance method at the same time.
<syntaxhighlight lang='go'>
i.SomeMethod() // prints "invocation with a nil dynamic value"
</syntaxhighlight>
An interface value that carries a dynamic type but no dynamic value is a legal state. It is a way to implement static (type) methods.


The method set associated with the pointers of a type always includes the method set associated with the values of the type.
In the case of an interface variable with <code>nil</code> dynamic value, <code>i == nil</code> evaluates to <code>false</code> but <code>testify</code> <code>require.Nil()</code> evaluates to <code>true</code>.


===Why the Difference between Method Sets associated with Values and Pointers===
<span id='Nil_Dynamic_Value_Testing_on_Private_Types'></span>This is how to test public interfaces whose implementing type is package-private: {{Internal|Go_Nil_Dynamic_Value_Testing_on_Private_Types#Overview|Testing Public Interfaces whose Implementing Type is Package-Private}}


<font color=red>
==<tt>nil</tt> Interface Value==
It is not always possible to get the address of a value, but it is always possible to get the value an address points to.
This describes an interface value with a <code>nil</code> dynamic type. In this situation, you cannot call the methods on that interface, because without a dynamic type you can't know which method you are referring to.
==<span id='Type_Assertions'></span>Type Assertion==
There is syntax in the language which, given an interface variable, allows us to extract the [[#Dynamic_Value|dynamic value]] with the actual [[#Dynamic_Type|dynamic type]]: {{Internal|Go_Type_Assertion#Overview|Type Assertion}}


'''TODO''' more, page 130</font>
==Type Switch==
{{Internal|Go Type Switch#Overview|Type Switch}}


=When does a Type/Pointer Type Implement an Interface?=
=<span id='Interfaces_as_Function_Parameters_and_Return_Values'></span>Interfaces as Function Parameters and Result Values=
 
==Interface as Function Parameter==
Note that notion of a type implementing an interface and its corresponding pointer type implementing the same interface both make sense, and they are subtly different.
Interfaces can be used as arguments to functions. Passing an interface instance insures that the function body can rely on the fact the interface methods are available on the passed instance. When a function is declared with an interface type parameter, the function can be invoked with either a value or a pointer of the implementing type, as long the implementation uses a value receiver type. For the following interface and type declaration:
 
<syntaxhighlight lang='go'>
According to the language specification, a user-defined type <tt>T</tt> implements an interface if its [[#User-Defined_Type_Method_Set|method set]] (all methods declared with ''a value receiver'' of type <tt>T</tt>) is a superset of the [[#Interface|interface]].
type SomeInterface interface {
 
     MethodA()
By extrapolation, a pointer type implements an interface when the method set associated with pointers to that type is a superset of the [[#Interface|interface]]. As per [[Go_Interfaces#User-Defined_Type_Method_Set|User-Defined Type Method Set]] section, the method set associated with the pointer of a type include implicitly the method set associated with the value of the type.  
For more details about value and pointer receivers, see:
 
<blockquote style="background-color: #f9f9f9; border: solid thin lightgrey;">
:[[Go_Concepts_-_Functions#Value_and_Pointer_Receivers|Value and Pointer Receivers]]
</blockquote>
 
For more details about pointer types see:
 
<blockquote style="background-color: #f9f9f9; border: solid thin lightgrey;">
:[[Go_Concepts_-_The_Type_System#Pointer_Types|Pointer Types]]
</blockquote>
 
=Example=
 
The example that follows shows how a type <tt>B</tt> implements interface <tt>A</tt>:
 
<pre>
//
// The interface A declares an interface method set containing a single method m()
//
type A interface {
     m()
}
}


//
type SomeImplementation struct {
// At this point, the type B is not yet linked to the interface A in any way.
     data string
// Its method set is empty, so it does not implement interface A, it does not
// implement any interface.
//
type B struct {
     i int
}
}
 
</syntaxhighlight>
//
the type may chose to implement the interface by using a value receiver:
// We make B implement interface A by declaring B as a value receiver for m().
<syntaxhighlight lang='go'>
// This means B's method set includes now m() and it is a superset of A's method
func (s SomeImplementation) MethodA() {
// set.
     fmt.Printf("data: %s\n", s.data)
//
func (b B) m() {
    // simply reports the value of i
     fmt.Println(b.i)
}
}
</syntaxhighlight>


...
In this case, if a function is declared with a parameter of <code>SomeInterface</code> interface type, the function can be invoked by passing either a value or a pointer to the type instance:


//
<syntaxhighlight lang='go'>
// B now implements A, so B instances can be assigned to A variables
func SomeFunc(i SomeInterface) {
//
    i.MethodA()
var a A
}
a = B{1}
a.m()


...
...
</pre>


==A Variation of the Example==
t := SomeImplementation{data: "blue"}
SomeFunc(t)


Note that if in the example above we declare:
// this also works:
t2 := &SomeImplementation{data: "red"}
SomeFunc(t2)
</syntaxhighlight>


<pre>
If the implementation of the interface is done with a pointer receiver:
func (b *B) m() {
<syntaxhighlight lang='go'>
     ...
func (s *SomeImplementation) MethodA() {
     fmt.Printf("data: %s\n", s.data)
}
}
</pre>
</syntaxhighlight>


instead of <tt>func (b B) m() ...</tt>, then the <tt>a = B{1}</tt> assignment would fail at compile time with:
then only a pointer to the implementing type instance can be passed to the function <code>SomeFunc()</code>:


<pre>
<syntaxhighlight lang='go'>
./main.go:23: cannot use B literal (type B) as type A in assignment:
t := &SomeImplementation{data: "green"}
B does not implement A (m method has pointer receiver)
SomeFunc(t)
</pre>
</syntaxhighlight>


This is because the type B does not implement the interface, because m() is not in the type B method set.
If we attempt to pass an implementing type instance value, the compiler complains:
<font size=-2>
Cannot use t (variable of type SomeImplementation) as SomeInterface value in argument to SomeFunc: SomeImplementation does not implement SomeInterface (method MethodA has pointer receiver)
</font>


However, if we try:
==Interface as Function Result==
<pre>
Functions can return interfaces as results, as shown below. However, in general, functions [[#accept_interfaces_return_structs |should accept interfaces and return structs]].
var a A
<syntaxhighlight lang='go'>
a = &B{1}
type SomeInterface interface {
a.m()
  ...
</pre>
}


it works, because the pointer type B does implement the interface.
type SomeImplementation struct {
  ...
}


=Passing Interfaces to Functions=
// the type SomeImplementation  is presumably declared as receiver type for all interface methods, thus implements it


Interfaces can be used as arguments to functions. Passing an interface instance insures that the function body can rely on the fact the interface methods are available on the passed instance.
func someFunc() SomeInterface {
  return &SomeImplementation{} // this is a valid function declaration
}
</syntaxhighlight>


With the example above, we declare a function f() that expects an interface A and we pass a B instance when invoking the function:
=<span id='Empty_Interface'></span><span id='any'></span>Empty Interface <tt>interface{}</tt> <tt>any</tt>=
 
An empty interface, declared as <code>interface{}</code> or <code>any</code> is an interface that specifies no methods. Any type can "implement" that interface. If you want to declare a parameter of any type for a function, make it an empty interface:
<pre>
<syntaxhighlight lang='go'>
...
func SomeFunc(v interface{}) {
 
  ...
func f(a A) {
}
    a.m()
</syntaxhighlight>
<syntaxhighlight lang='go'>
func SomeFunc(v any) {
  ...
}
}
</syntaxhighlight>
According to the <code>builtin</code> package documentation, <code>any</code> is an alias for <code>interface{}</code> and it is equivalent to <code>interface{}</code> in all ways.


...
Rob Pike does not like empty interfaces: when you're programming and you use an empty interface, think really hard if this is actually what you want, or whether isn't just a little something you can put in, an interface with an actual method that is really necessary to capture the things you are talking about.
b := B{1}
f(b)
...
</pre>


Note that the B instance can be passed by value (as in the example above) or by reference (as in the example below). Both cases work:
=<span id='Embedded_Type'></span><span id='Embedded_Types'></span>Interfaces as Embedded Types=
<font color=darkkhaki>Interface names can be used as [[Go_Structs#Embedded_Type|embedded types]] in structs.</font>


<pre>
=Interface Inheritance=
...
{{Internal|Go_Language_Object_Oriented_Programming#Behavior_Inheritance|Behavior Inheritance}}
b := B{1}
f(&b)
...
</pre>


or (same thing)
=Using Interfaces=
 
==Function with Multiple Types of Parameters==
<pre>
Interface expresses similarity, so make the function take as a parameter an interface that expresses the similarity of all desired arguments.
...
<syntaxhighlight lang='go'>
bPtr := new(B)
func Area(s Shape) {
f(bPtr)
  ...
...
}
</pre>
</syntaxhighlight>
 
==Mocking==
<font color=red>'''TODO''' further discussion on the merits of passing the interface by value and by reference.</font>
See: {{Internal|Github.com/stretchr/testify#Mocks|Testify Mocks}}
 
=Interfaces as Fields=
 
Interfaces can be used as [[Go_Structs#Fields|fields]] in <tt>[[Go_Structs|struct]]</tt>s.
 
=Implementation Details=
 
An interface variable is a two-word data structure.
 
The first word contains a pointer to an internal table called ''iTable'', which contains type information about the stored value: the type of the value that has been stored (the concrete type) and a list of methods associated with the value.
 
The second word is a reference to the stored value.
 
The combination of type information and pointer binds the relationship between the two values.
 
More details "Go in Action" page 125.

Latest revision as of 22:18, 5 November 2024

External

Internal

Overview

An interface is a set of method signatures. The interface is declared with the keyword type, followed by the name of the interface, followed by the keyword interface and by the method signatures enclosed in a curly brace block. Each signature contains the method name, optional parameter names and mandatory parameter types, and optional return variable names and mandatory return types. Providing parameter and return names is recommended, as it arguably exposes more semantics, making the code clearer. See example, below.

The interface declaration contains no method implementations.

Interfaces are not fully-featured types, they're less than that, but they're used to express conceptual similarities between types. If several types implement the same interface, they are similar in a way that is important to the application. An interface highlights the similarities between the types implementing it and conceals the differences.

An interface does need to be formally declared on a target type for that type to implement it. Interfaces are satisfied implicitly. A type implements or satisfies an interface if the type defines all methods specified in the interface. Of course, the type can have other methods not specified by the interface. This is called duck typing. If a type implements an interface by the virtue of duck typing, a value of the type can be stored in a value of that interface type and the compiler ensure the assignments are correct at compile-time. A type can implement multiple interfaces.

Interfaces can be thought of a kind of conceptual inheritance: types inherit behavior specified by an interface. Interfaces help supporting polymorphism in Go: different types implement different behaviors specified by the same interface. For more details see:

Go Inheritance and Polymorphism

Go interfaces allow programs to model behavior rather than model types. The behavior is "attached" to existing types, via methods, and independently, the behavior is given a name, the name of the interface that groups together the methods. Go type system is structural, not nominal. The interfaces are behavior contracts, and they belong in the consuming code, not in the producing code. A common pattern should be to accept interfaces and return structs.

This is debatable: for an interface itself, it feels natural that the methods should return the same interface, not an implementing type. What if the implementing type is package private, and the interface is public?

A type and its associated pointer type may or may not implement the same interface, depending on how the methods are associated with the value and pointer receivers of that type.

"The bigger the interface, the weaker the abstraction." The most important thing about interfaces in Go is that the smaller the interface is, the more useful it is. This is a Go-specific idea: we want to make little interfaces, so we can build components that share them.

Interfaces are defined at the "fault lines" of the system's architecture. These are between the main() function and the rest, and along the package API boundaries.

Interfaces should not be defined before a realistic example of usage exists.

Interfaces are rigid. For a library that exposes interfaces in the public API, any change to the interface requires releasing a new major version, since it breaks all third-party implementations.

Is that right? What if we add to the interface?

Declaration

The following syntax declares an interface:

type <InterfaceName> interface {
  // Interface method set:
  <method_signature_1>
  <method_signature_2>
  ...
}

Example:

type SomeInterface interface {
  MethodA(int) (int, error)                          // No parameter and return value names are specified.
  MethodB(someValue int) (someResult int, err error) // Parameter and return value names are provided.
                                                     // This is arguably clearer, as it conveys more semantics.
}

What results after such a declaration is an interface type. For a discussion of concrete type vs. interface types, see:

Go Language | Concrete vs. Interface Types

Interface Method Set

An interface method set the list of method declaration specified in the interface type declaration. The interface type declaration has the only purpose of defining the method set and giving it a name, which is the interface name. Also see:

Method Sets

Naming

Single-method interfaces are named using the method name plus an "er" suffix. An interface with just one Write method would be called Writer. Sometimes, the result isn't correct English, but we do it anyway. In other cases, we use English to make the result nicer:

type ByteReader interface {
    ReadByte() (c byte, err error)
}

When an interface includes multiple methods, choose a name that accurately describes its purpose.

Also see:

Go Style

Implementing an Interface

As mentioned above, interfaces are not formally declared to be satisfied with dedicated syntax, they are satisfied implicitly. The type satisfying an interface do not need to formally declare the name of the interface anywhere in its definition. Instead, it is sufficient to declare themselves as value or pointer receiver types for all the methods in the interface declaration.

type SomeImplementation { ... } // SomeInterface is specified nowhere in the SomeImplementation declaration

// Declaring the type as pointer receiver for *all* methods declared in the interface makes 
// the type to implement that interface . Value and pointer receivers must not be mixed by 
// the method implementations of an interface.
func (s *SomeImplementation) MethodA(i int) (int, error) { ... }
func (s *SomeImplementation) MethodB(someValue int) (int, error) { ... }

The method signatures from the interface method set are independent on whether the corresponding methods from the concrete types are declared with value or pointer receivers.

When Does a Type/Pointer Type Implement an Interface?

Note that notion of a type implementing an interface and its corresponding pointer type implementing the same interface both make sense, and they are subtly different. According to the language specification, a user-defined type T implements an interface if its method set, which are all methods declared with a value receiver of type T, is a superset of the interface. By extrapolation, a pointer type implements an interface when the method set associated with pointers to that type is a superset of the interface. As explained here, the method set associated with the pointer of a type includes implicitly the method set associated with the value of the type.

What Can Implement an Interface?

Structs can implement interfaces.

Type aliases can implement interfaces. In general, named types can implement interfaces, like in the following example where a bool can implement an interface as long as it is named:

type SomeInterface interface {
    SomeMethod()
}

type someImpl bool

func (s someImpl) SomeMethod() {
	fmt.Printf("SomeMethod\n")
}

someImpl(true).SomeMethod()

What else? Can a function be used as an interface?

Interface Values

Declaring a variable of an interface type creates an interface value:

var i SomeInterface // i is a pair: (dynamic_type, dynamic_value)

The interface value contains a data pair, including a dynamic type and a dynamic value. The dynamic type, when set, is a concrete type that implements that interface. The dynamic value, when set, is an instance of the dynamic type. Internally, an interface variable is a two-word data structure. The first word contains a pointer to an internal table called iTable, which contains type information about the stored value: the concrete type of the value that has been stored and a list of methods associated with the value. The second word is a reference to the stored value. The combination of type information and pointer binds the relationship between the two values.

In the case of the above declaration, both elements of the pair are nil:

fmt.Printf("%v\n", i) // prints <nil>

Assuming the following interface and concrete type declarations:

type SomeInterface interface {
  SomeMethod()
}

type SomeImplementation struct {
  data string
}

// SomeMethod makes SomeImplementation implement SomeInterface.
func (s *SomeImplementation) SomeMethod() {
  if s == nil {
    fmt.Println("invocation with a nil dynamic value")
  } else {
    fmt.Printf("data: %s\n", s.data)    
  }
}

... the following variable declaration and initialization ends up in an interface variable whose both dynamic type and dynamic value are set to *SomeImplementation and &SomeImplementation{"test"}, respectively:

var i SomeInterface
i = &SomeImplementation{"test"}

The interface value can be then used to invoke the SomeMethod() method on the dynamic value, which was assigned to the dynamic concrete type instance pointer.

i.SomeMethod() // prints "data: test"

An interface value can have a nil dynamic value, and still be usable, as long it is assigned a dynamic type with this syntax:

var i SomeInterface
var t *SomeImplementation
i = t

An interface value assigned with a pointer to the dynamic type, but not with a dynamic value can still be invoked into, given that the method implementation cases for the situation when the receiver type implicit argument is nil. In the example above, we assigned i a dynamic type *SomeImplementation but not a dynamic value, as t has no concrete value yet. The dynamic type is enough information to go find which implementation SomeMethod() should use.


This is why it is a good idea to guard against nil implicit argument in all concrete methods provided by types that implement interfaces.

This way we get a static method, in the Java or Python sense of the word and an instance method at the same time.

i.SomeMethod() // prints "invocation with a nil dynamic value"

An interface value that carries a dynamic type but no dynamic value is a legal state. It is a way to implement static (type) methods.

In the case of an interface variable with nil dynamic value, i == nil evaluates to false but testify require.Nil() evaluates to true.

This is how to test public interfaces whose implementing type is package-private:

Testing Public Interfaces whose Implementing Type is Package-Private

nil Interface Value

This describes an interface value with a nil dynamic type. In this situation, you cannot call the methods on that interface, because without a dynamic type you can't know which method you are referring to.

Type Assertion

There is syntax in the language which, given an interface variable, allows us to extract the dynamic value with the actual dynamic type:

Type Assertion

Type Switch

Type Switch

Interfaces as Function Parameters and Result Values

Interface as Function Parameter

Interfaces can be used as arguments to functions. Passing an interface instance insures that the function body can rely on the fact the interface methods are available on the passed instance. When a function is declared with an interface type parameter, the function can be invoked with either a value or a pointer of the implementing type, as long the implementation uses a value receiver type. For the following interface and type declaration:

type SomeInterface interface {
    MethodA()
}

type SomeImplementation struct {
    data string
}

the type may chose to implement the interface by using a value receiver:

func (s SomeImplementation) MethodA() {
    fmt.Printf("data: %s\n", s.data)
}

In this case, if a function is declared with a parameter of SomeInterface interface type, the function can be invoked by passing either a value or a pointer to the type instance:

func SomeFunc(i SomeInterface) {
    i.MethodA()
}

...

t := SomeImplementation{data: "blue"}
SomeFunc(t)

// this also works:
t2 := &SomeImplementation{data: "red"}
SomeFunc(t2)

If the implementation of the interface is done with a pointer receiver:

func (s *SomeImplementation) MethodA() {
    fmt.Printf("data: %s\n", s.data)
}

then only a pointer to the implementing type instance can be passed to the function SomeFunc():

t := &SomeImplementation{data: "green"}
SomeFunc(t)

If we attempt to pass an implementing type instance value, the compiler complains:

Cannot use t (variable of type SomeImplementation) as SomeInterface value in argument to SomeFunc: SomeImplementation does not implement SomeInterface (method MethodA has pointer receiver)

Interface as Function Result

Functions can return interfaces as results, as shown below. However, in general, functions should accept interfaces and return structs.

type SomeInterface interface {
  ...
}

type SomeImplementation struct {
  ... 
}

 // the type SomeImplementation  is presumably declared as receiver type for all interface methods, thus implements it 

func someFunc() SomeInterface {
  return &SomeImplementation{} // this is a valid function declaration
}

Empty Interface interface{} any

An empty interface, declared as interface{} or any is an interface that specifies no methods. Any type can "implement" that interface. If you want to declare a parameter of any type for a function, make it an empty interface:

func SomeFunc(v interface{}) {
  ...
}
func SomeFunc(v any) {
  ...
}

According to the builtin package documentation, any is an alias for interface{} and it is equivalent to interface{} in all ways.

Rob Pike does not like empty interfaces: when you're programming and you use an empty interface, think really hard if this is actually what you want, or whether isn't just a little something you can put in, an interface with an actual method that is really necessary to capture the things you are talking about.

Interfaces as Embedded Types

Interface names can be used as embedded types in structs.

Interface Inheritance

Behavior Inheritance

Using Interfaces

Function with Multiple Types of Parameters

Interface expresses similarity, so make the function take as a parameter an interface that expresses the similarity of all desired arguments.

func Area(s Shape) {
  ...
}

Mocking

See:

Testify Mocks