_
as its name.
error
interface type, which definition is shown below, embeds a method specification Error() string
. In the definition, interface{...}
is called an interface type literal and the word interface
are a keyword in Go.
type error interface {
Error() string
}
error
interface type (directly) specified a method Error() string
. Its type set is composed of all non-interface types which have a method with the specification Error() string
. In theory, the type set is infinite. Surely, for a specified Go project, it is finite.
// This interface directly specifies two methods and
// embeds two other interface types, one of which
// is a type name and the other is a type literal.
type ReadWriteCloser = interface {
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
error // a type name
interface{ Close() error } // a type literal
}
// This interface embeds an approximation type. Its type
// set includes all types whose underlying type is []byte.
type AnyByteSlice = interface {
~[]byte
}
// This interface embeds a type union. Its type set includes
// 6 types: uint, uint8, uint16, uint32, uint64 and uintptr.
type Unsigned interface {
uint | uint8 | uint16 | uint32 | uint64 | uintptr
}
ReadWriteCloser
is equivalent to the interface type denoted by the following literal, which directly specifies four methods.
interface {
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
Error() string
Close() error
}
error
interface type.
// The unnamed blank interface type.
interface{}
// Nothing is a defined blank interface type.
type Nothing interface{}
any
, which denotes the blank interface type interface{}
.
ReadWriteCloser
contains four methods.
error
contains only one method.
ReadWriteCloser
is a basic type, but the Unsigned
interface type and the type denoted by alias AnyByteSlice
are not. The latter two are both constraint-only interface types.
error
interface type are also all basic interface types.
T
implements an interface type X
, then the method set of T
must be superset of X
, whether T
is an interface type or an non-interface type. Generally, not vice versa. But if X
is a basic interface, then vice versa. For example, in the examples provided in a previous section, the interface type denoted by ReadWriteCloser
implements the error
interface type.
implements
keyword in Go. Go compilers will check the implementation relations automatically as needed.
*Book
, integer type MyInt
and pointer type *MyInt
all contain the method specification About() string
, so they all implement the above mentioned interface type Aboutable
.
type Aboutable interface {
About() string
}
type Book struct {
name string
// more other fields ...
}
func (book *Book) About() string {
return "Book: " + book.name
}
type MyInt int
func (MyInt) About() string {
return "I'm a custom integer value"
}
DB
and type Tx
declared in the database/sql
standard package will both implement the interface type automatically, for they both have the three corresponding methods specified in the interface.
import "database/sql"
...
type DatabaseStorer interface {
Exec(query string, args ...interface{}) (sql.Result, error)
Prepare(query string) (*sql.Stmt, error)
Query(query string, args ...interface{}) (*sql.Rows, error)
}
T
implements a (basic) interface type I
, then any value of type T
can be implicitly converted to type I
. In other words, any value of type T
is assignable to (modifiable) values of type I
. When a T
value is converted (assigned) to an I
value,
T
is a non-interface type, then a copy of the T
value is boxed (or encapsulated) into the result (or destination) I
value. The time complexity of the copy is %% O %%(n)
, where n
is the size of copied T
value.
T
is also an interface type, then a copy of the value boxed in the T
value is boxed (or encapsulated) into the result (or destination) I
value. The standard Go compiler makes an optimization here, so the time complexity of the copy is O(1)
, instead of O(n)
.
nil
identifier. Nothing is boxed in a nil interface value. Assigning an untyped nil
to an interface value will clear the dynamic value boxed in the interface value.
nil
in Go. Non-interface nil values can also be boxed in interface values. An interface value boxing a nil non-interface value still boxes something, so it is not a nil interface value.)
any
type in many other languages.
nil
values) is assigned to a blank interface value, the untyped value will be first converted to its default type. (In other words, we can think the untyped value is deduced as a value of its default type).
package main
import "fmt"
type Aboutable interface {
About() string
}
// Type *Book implements Aboutable.
type Book struct {
name string
}
func (book *Book) About() string {
return "Book: " + book.name
}
func main() {
// A *Book value is boxed into an
// interface value of type Aboutable.
var a Aboutable = &Book{"Go 101"}
fmt.Println(a) // &{Go 101}
// i is a blank interface value.
var i interface{} = &Book{"Rust 101"}
fmt.Println(i) // &{Rust 101}
// Aboutable implements interface{}.
i = a
fmt.Println(i) // &{Go 101}
}
fmt.Println
function used many times in previous articles is
func Println(a ...interface{}) (n int, err error)
fmt.Println
function calls can take arguments of any types.
package main
import "fmt"
func main() {
var i interface{}
i = []int{1, 2, 3}
fmt.Println(i) // [1 2 3]
i = map[string]int{"Go": 2012}
fmt.Println(i) // map[Go:2012]
i = true
fmt.Println(i) // true
i = 1
fmt.Println(i) // 1
i = "abc"
fmt.Println(i) // abc
// Clear the boxed value in interface value i.
i = nil
fmt.Println(i) // <nil>
}
t
of a type T
is boxed in an interface value i
of type I
, calling a method specified by the interface type I
on the interface value i
will call the corresponding method declared for the non-interface type T
on the non-interface value t
actually. In other words, calling the method of an interface value will actually call the corresponding method of the dynamic value of the interface value. For example, calling method i.m
will call method t.m
actually. With different dynamic values of different dynamic types boxed into the interface value, the interface value behaves differently. This is called polymorphism.
i.m
is called, the method table in the implementation information stored in i
will be looked up to find and call the corresponding method t.m
. The method table is a slice and the lookup is just a slice element indexing, so this is quick.
package main
import "fmt"
type Filter interface {
About() string
Process([]int) []int
}
// UniqueFilter is used to remove duplicate numbers.
type UniqueFilter struct{}
func (UniqueFilter) About() string {
return "remove duplicate numbers"
}
func (UniqueFilter) Process(inputs []int) []int {
outs := make([]int, 0, len(inputs))
pusheds := make(map[int]bool)
for _, n := range inputs {
if !pusheds[n] {
pusheds[n] = true
outs = append(outs, n)
}
}
return outs
}
// MultipleFilter is used to keep only
// the numbers which are multiples of
// the MultipleFilter as an int value.
type MultipleFilter int
func (mf MultipleFilter) About() string {
return fmt.Sprintf("keep multiples of %v", mf)
}
func (mf MultipleFilter) Process(inputs []int) []int {
var outs = make([]int, 0, len(inputs))
for _, n := range inputs {
if n % int(mf) == 0 {
outs = append(outs, n)
}
}
return outs
}
// With the help of polymorphism, only one
// "filterAndPrint" function is needed.
func filterAndPrint(fltr Filter, unfiltered []int) []int {
// Calling the methods of "fltr" will call the
// methods of the value boxed in "fltr" actually.
filtered := fltr.Process(unfiltered)
fmt.Println(fltr.About() + ":\n\t", filtered)
return filtered
}
func main() {
numbers := []int{12, 7, 21, 12, 12, 26, 25, 21, 30}
fmt.Println("before filtering:\n\t", numbers)
// Three non-interface values are boxed into
// three Filter interface slice element values.
filters := []Filter{
UniqueFilter{},
MultipleFilter(2),
MultipleFilter(3),
}
// Each slice element will be assigned to the
// local variable "fltr" (of interface type
// Filter) one by one. The value boxed in each
// element will also be copied into "fltr".
for _, fltr := range filters {
numbers = filterAndPrint(fltr, numbers)
}
}
before filtering: [12 7 21 12 12 26 25 21 30] remove duplicate numbers: [12 7 21 26 25 30] keep multiples of 2: [12 26 30] keep multiples of 3: [12 30]
filterAndPrint
function for each filter type.
reflect
standard package. Please read reflections in Go to get how to use that package. Below will only introduce the built-in reflection functionalities in Go. In Go, built-in reflections are achieved with type assertions and type-switch
control flow code blocks.
i.(T)
, where i
is an interface value and T
is a type name or a type literal. Type T
must be
i.(T)
, i
is called the asserted value and T
is called the asserted type. A type assertion might succeed or fail.
T
being a non-interface type, if the dynamic type of i
exists and is identical to T
, then the assertion will succeed, otherwise, the assertion will fail. When the assertion succeeds, the evaluation result of the assertion is a copy of the dynamic value of i
. We can view assertions of this kind as value unboxing attempts.
T
being an interface type, if the dynamic type of i
exists and implements T
, then the assertion will succeed, otherwise, the assertion will fail. When the assertion succeeds, a copy of the dynamic value of i
will be boxed into a T
value and the T
value will be used as the evaluation result of the assertion.
package main
import "fmt"
func main() {
// Compiler will deduce the type of 123 as int.
var x interface{} = 123
// Case 1:
n, ok := x.(int)
fmt.Println(n, ok) // 123 true
n = x.(int)
fmt.Println(n) // 123
// Case 2:
a, ok := x.(float64)
fmt.Println(a, ok) // 0 false
// Case 3:
a = x.(float64) // will panic
}
package main
import "fmt"
type Writer interface {
Write(buf []byte) (int, error)
}
type DummyWriter struct{}
func (DummyWriter) Write(buf []byte) (int, error) {
return len(buf), nil
}
func main() {
var x interface{} = DummyWriter{}
var y interface{} = "abc"
// Now the dynamic type of y is "string".
var w Writer
var ok bool
// Type DummyWriter implements both
// Writer and interface{}.
w, ok = x.(Writer)
fmt.Println(w, ok) // {} true
x, ok = w.(interface{})
fmt.Println(x, ok) // {} true
// The dynamic type of y is "string",
// which doesn't implement Writer.
w, ok = y.(Writer)
fmt.Println(w, ok) // <nil> false
w = y.(Writer) // will panic
}
i
with a dynamic type T
, the method call i.m(...)
is equivalent to the method call i.(T).m(...)
.
type-switch
control flow block
type-switch
code block syntax may be the weirdest syntax in Go. It can be viewed as the enhanced version of type assertion. A type-switch
code block is in some way similar to a switch-case
control flow code block. It looks like:
switch aSimpleStatement; v := x.(type) {
case TypeA:
...
case TypeB, TypeC:
...
case nil:
...
default:
...
}
aSimpleStatement;
portion is optional in a type-switch
code block. aSimpleStatement
must be a simple statement. x
must be an interface value and it is called the asserted value. v
is called the assertion result, it must be present in a short variable declaration form.
case
keyword in a type-switch
block can be followed by the predeclared nil
identifier or a comma-separated list composed of at least one type name and type literal. None of such items (nil
, type names and type literals) may be duplicate in the same type-switch
code block.
case
keyword in a type-switch
code block is not an interface type, then it must implement the interface type of the asserted value.
type-switch
control flow code block is used.
package main
import "fmt"
func main() {
values := []interface{}{
456, "abc", true, 0.33, int32(789),
[]int{1, 2, 3}, map[int]bool{}, nil,
}
for _, x := range values {
// Here, v is declared once, but it denotes
// different variables in different branches.
switch v := x.(type) {
case []int: // a type literal
// The type of v is "[]int" in this branch.
fmt.Println("int slice:", v)
case string: // one type name
// The type of v is "string" in this branch.
fmt.Println("string:", v)
case int, float64, int32: // multiple type names
// The type of v is "interface{}",
// the same as x in this branch.
fmt.Println("number:", v)
case nil:
// The type of v is "interface{}",
// the same as x in this branch.
fmt.Println(v)
default:
// The type of v is "interface{}",
// the same as x in this branch.
fmt.Println("others:", v)
}
// Note, each variable denoted by v in the
// last three branches is a copy of x.
}
}
number: 456 string: abc others: true number: 0.33 number: 789 int slice: [1 2 3] others: map[] <nil>
package main
import "fmt"
func main() {
values := []interface{}{
456, "abc", true, 0.33, int32(789),
[]int{1, 2, 3}, map[int]bool{}, nil,
}
for _, x := range values {
if v, ok := x.([]int); ok {
fmt.Println("int slice:", v)
} else if v, ok := x.(string); ok {
fmt.Println("string:", v)
} else if x == nil {
v := x
fmt.Println(v)
} else {
_, isInt := x.(int)
_, isFloat64 := x.(float64)
_, isInt32 := x.(int32)
if isInt || isFloat64 || isInt32 {
v := x
fmt.Println("number:", v)
} else {
v := x
fmt.Println("others:", v)
}
}
}
}
type-switch
code blocks are similar to switch-case
code blocks in some aspects.
switch-case
blocks, in a type-switch
code block, there can be at most one default
branch.
switch-case
blocks, in a type-switch
code block, if the default
branch is present, it can be the last branch, the first branch, or a middle branch.
switch-case
blocks, a type-switch
code block may not contain any branches, it will be viewed as a no-op.
switch-case
code blocks, fallthrough
statements can't be used within branch blocks of a type-switch
code block.
I
) of the interface value, so the non-interface value can be converted to (boxed into) an interface value of I
. This means a comparison between a non-interface value and an interface value can be translated to a comparison between two interface values. So below only comparisons between two interface values will be explained.
==
operator):
false
.
nil
may be not equal. An example:
package main
import "fmt"
func main() {
var a, b, c interface{} = "abc", 123, "a"+"b"+"c"
// A case of step 2.
fmt.Println(a == b) // false
// A case of step 3.
fmt.Println(a == c) // true
var x *int = nil
var y *bool = nil
var ix, iy interface{} = x, y
var i interface{} = nil
// A case of step 2.
fmt.Println(ix == iy) // false
// A case of step 1.
fmt.Println(ix == i) // false
// A case of step 1.
fmt.Println(iy == i) // false
// []int is an incomparable type
var s []int = nil
i = s
// A case of step 1.
fmt.Println(i == nil) // false
// A case of step 3.
fmt.Println(i == i) // will panic
}
[]T
can't be directly converted to []I
, even if type T
implements interface type I
.
[]string
value to []interface{}
type. Unlike some other languages, there is no direct way to make the conversion. We must make the conversion manually in a loop:
package main
import "fmt"
func main() {
words := []string{
"Go", "is", "a", "high",
"efficient", "language.",
}
// The prototype of fmt.Println function is
// func Println(a ...interface{}) (n int, err error).
// So words... can't be passed to it as the argument.
// fmt.Println(words...) // not compile
// Convert the []string value to []interface{}.
iw := make([]interface{}, 0, len(words))
for _, w := range words {
iw = append(iw, w)
}
fmt.Println(iw...) // compiles okay
}
m
in the method set defined by an interface type I
, compilers will implicitly declare a function named I.m
, which has one more input parameter, of type I
, than method m
. The extra parameter is the first input parameter of function I.m
. Assume i
is an interface value of I
, then the method call i.m(...)
is equivalent to the function call I.m(i, ...)
.
package main
import "fmt"
type I interface {
m(int)bool
}
type T string
func (t T) m(n int) bool {
return len(t) > n
}
func main() {
var i I = T("gopher")
fmt.Println(i.m(5)) // true
fmt.Println(I.m(i, 5)) // true
fmt.Println(interface{m(int)bool}.m(i, 5)) // true
// The following lines compile okay,
// but will panic at run time.
I(nil).m(5)
I.m(nil, 5)
interface {m(int) bool}.m(nil, 5)
}
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.
reflect
standard package.sync
standard package.sync/atomic
standard package.