builtin
and unsafe
standard code packages. Built-in functions have several differences from custom functions. These differences will be mentioned below.
func
keyword and a function signature literal. A function signature is composed of two type list, one is the input parameter type list, the other is the output result type lists. Parameter and result names can appear in function type and signature literals, but the names are not important.
func
keyword can be presented in signature literals, or not. For this reason, we can think function type and function signature as the same concept.
func (a int, b string, c string) (x int, y int, z bool)
func (a int, b, c string) (x, y int, z bool)
func (x int, y, z string) (a, b int, c bool)
_
. The above ones are equivalent to the following one.
func (_ int, _, _ string) (_, _ int, _ bool)
func (int, string, string) (int, int, bool) // the standard form
func (a int, b string, c string) (int, int, bool)
func (x int, _ string, z string) (int, int, bool)
func (int, string, string) (x int, y int, z bool)
func (int, string, string) (a int, b int, _ bool)
()
in a literal, even if the parameter list is blank. If a result list of a function type is blank, then it can be omitted from literal of the function type. When a result list has most one result, then the result list doesn't need to be enclosed in a ()
if the literal of the result list contains no result names.
// The following three function types are identical.
func () (x int)
func () (int)
func () int
// The following two function types are identical.
func (a int, b string) ()
func (a int, b string)
...
to the element type of its (slice) type in its declaration. Example:
func (values ...int64) (sum int64)
func (sep string, tokens ...string) string
nil
identifier. (Function values will be explained in the last section of the current article.)
func
keyword, a function name and the literal of a function signature literal.
func Double(n int) (result int)
// Sum and return the input numbers.
func Sum(values ...int64) (sum int64) {
// The type of values is []int64.
sum = 0
for _, v := range values {
sum += v
}
return
}
// An inefficient string concatenation function.
func Concat(sep string, tokens ...string) string {
// The type of tokens is []string.
r := ""
for i, t := range tokens {
if i != 0 {
r += sep
}
r += t
}
return r
}
...T
, then the type of the parameter is []T
actually.
Print
, Println
and Printf
functions in the fmt
standard package are all variadic functions.
func Print(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)
[]interface{}
, which element type interface{}
is an interface types. Interface types and values will be explained interfaces in Go later.
[]T
:
[]T
, and the slice must be followed by three dots ...
. The passed slice is called as a variadic argument.
T
. These arguments will be copied (or converted) as the elements of a new allocated slice value of type []T
, then the new allocated slice will be passed to the variadic parameter.
package main
import "fmt"
func Sum(values ...int64) (sum int64) {
sum = 0
for _, v := range values {
sum += v
}
return
}
func main() {
a0 := Sum()
a1 := Sum(2)
a3 := Sum(2, 3, 5)
// The above three lines are equivalent to
// the following three respective lines.
b0 := Sum([]int64{}...) // <=> Sum(nil...)
b1 := Sum([]int64{2}...)
b3 := Sum([]int64{2, 3, 5}...)
fmt.Println(a0, a1, a3) // 0 2 10
fmt.Println(b0, b1, b3) // 0 2 10
}
package main
import "fmt"
func Concat(sep string, tokens ...string) (r string) {
for i, t := range tokens {
if i != 0 {
r += sep
}
r += t
}
return
}
func main() {
tokens := []string{"Go", "C", "Rust"}
// manner 1
langsA := Concat(",", tokens...)
// manner 2
langsB := Concat(",", "Go", "C","Rust")
fmt.Println(langsA == langsB) // true
}
package main
// See above examples for the full declarations
// of the following two functions.
func Sum(values ...int64) (sum int64)
func Concat(sep string, tokens ...string) string
func main() {
// The following two lines both fail
// to compile, for the same error:
// too many arguments in call.
_ = Sum(2, []int64{3, 5}...)
_ = Concat(",", "Go", []string{"C", "Rust"}...)
}
init
and the same type func ()
.
_
, in which cases, the declared functions can never be called.
unsafe
standard package are always evaluated at compile time. Calls to some other built-in functions, such as len
and cap
, may be evaluated at either compile time or run time, depending on the passed arguments. The results of the function calls evaluated at compile time can be assigned to constants.
*.a
files. A function implemented in Go assembly is still needed to be declared in a *.go
file, but the only the prototype of the function is needed to be present. The body portion of the declaration of the function must be omitted in the *.go
file.
return
terminating statement, there are some other kinds of terminating statements. So a function body is not required to contain a return statement. For example,
func fa() int {
a:
goto a
}
func fb() bool {
for{}
}
recover
and copy
, can't be discarded, though they can be ignored by assigning them to some blank identifiers. Function calls whose results can't be discarded can't be used as deferred function calls or goroutine calls.
package main
func HalfAndNegative(n int) (int, int) {
return n/2, -n
}
func AddSub(a, b int) (int, int) {
return a+b, a-b
}
func Dummy(values ...int) {}
func main() {
// These lines compile okay.
AddSub(HalfAndNegative(6))
AddSub(AddSub(AddSub(7, 5)))
AddSub(AddSub(HalfAndNegative(6)))
Dummy(HalfAndNegative(6))
_, _ = AddSub(7, 5)
// The following lines fail to compile.
/*
_, _, _ = 6, AddSub(7, 5)
Dummy(AddSub(7, 5), 9)
Dummy(AddSub(7, 5), HalfAndNegative(6))
*/
}
nil
identifier.
init
functions also can't be used as values.
package main
import "fmt"
func Double(n int) int {
return n + n
}
func Apply(n int, f func(int) int) int {
return f(n) // the type of f is "func(int) int"
}
func main() {
fmt.Printf("%T\n", Double) // func(int) int
// Double = nil // error: Double is immutable.
var f func(n int) int // default value is nil.
f = Double
g := Apply // let compile deduce the type of g
fmt.Printf("%T\n", g) // func(int, func(int) int) int
fmt.Println(f(9)) // 18
fmt.Println(g(6, Double)) // 12
fmt.Println(Apply(6, f)) // 12
}
g(6, Double)
and Apply(6, f)
are equivalent.
package main
import "fmt"
func main() {
// This function returns a function (a closure).
isMultipleOfX := func (x int) func(int) bool {
return func(n int) bool {
return n%x == 0
}
}
var isMultipleOf3 = isMultipleOfX(3)
var isMultipleOf5 = isMultipleOfX(5)
fmt.Println(isMultipleOf3(6)) // true
fmt.Println(isMultipleOf3(8)) // false
fmt.Println(isMultipleOf5(10)) // true
fmt.Println(isMultipleOf5(12)) // false
isMultipleOf15 := func(n int) bool {
return isMultipleOf3(n) && isMultipleOf5(n)
}
fmt.Println(isMultipleOf15(32)) // false
fmt.Println(isMultipleOf15(60)) // true
}
for-range
loops.
// K and V are some types.
func(yield func() bool)
func(yield func(V) bool)
func(yield func(K, V) bool)
for-range
loop operates on such an iterator function value, the iterator function value will be called (once) with an implicitly constructed yield
callback function. The yield
callback function returns a bool
result. When it returns false
, the call to the iterator function should (but is not required to) exit immediately; otherwise (when the yield
callback function returns true
), the iterator function should continue run until it exits naturally.
package main
import "fmt"
func Loop3(yield func() bool) {
for range 3 {
if (!yield()) {
return
}
}
}
func OneDigitNumbers(onValue func(int) bool) {
for i := range 10 {
if (!onValue(i)) {
return
}
}
}
func SquareLessThan50(onKeyValue func(int, int) bool) {
for i := range 8 {
if (!onKeyValue(i, i*i)) {
return
}
}
}
func main() {
var n = 0
for range Loop3 {
fmt.Print(n)
n++
}
fmt.Println()
// Output: 012
for i := range OneDigitNumbers {
fmt.Print(i)
}
fmt.Println()
// Output: 0123456789
for i, ii := range SquareLessThan50 {
fmt.Printf("%v:%v ", i, ii)
}
fmt.Println()
// Output: 0:0 1:1 2:4 3:9 4:16 5:25 6:36 7:49
}
for-range
loops are eqivalent to the following function calls, respectively.
func main() {
var n = 0
Loop3(func() bool {
fmt.Print(n)
n++
return true
})
fmt.Println()
OneDigitNumbers(func(i int) bool {
fmt.Print(i)
return true
})
fmt.Println()
SquareLessThan50(func(i, ii int) bool {
fmt.Printf("%v:%v ", i, ii)
return true
})
fmt.Println()
}
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.