Go function - working with functions in Golang (2023)

last modified August 24, 2023

Go function tutorial shows how to work with functions in Golang.

Go function definition

A function is a mapping of zero or more input parameters to zero or moreoutput parameters.

The advantages of using functions are:

  • Reducing duplication of code
  • Decomposing complex problems into simpler pieces
  • Improving clarity of the code
  • Reuse of code
  • Information hiding

Go functions are first-class citizens. Functions can be assigned to variables,passed as arguments to functions or returned from functions. This makes thelanguage more flexible.

Functions in Go are created with the func keyword. We use thereturn keyword to return values from functions. The body ofthe function consists of statements that are executed when the function iscalled. The body is delimited with a pair of curly brackets {}.To call a function, we specify its name followed by round bracktets(). A function may or may not take parameters.

$ go versiongo version go1.18.1 linux/amd64

We use Go version 1.18.

Go function simple example

The following example creates a simple function in Go.

simple.go

package mainimport "fmt"func main() { x := 4 y := 5 z := add(x, y) fmt.Printf("Output: %d\n", z)}func add(a int, b int) int { return a + b}

In the code example, we define a function which adds two values.

z := add(x, y)

We call the add function; it takes two parameters. The computedvalue is passed to the z variable.

func add(a int, b int) int { return a + b}

We define the add function. The parameters of the function areseparated with comma; each parameter name is followed with its data type.After the parameters, we specify the return value type. The statements that areexecuted when the function is called are placed between curly brackets.The result of the addition operation is returned to the caller with thereturn keyword.

$ go run simple.go Output: 9

Go function omit type

When the parameters of a function have the same type, the type can be omittedfor some of them; that is, specified only once.

omittype.go

package mainimport "fmt"func add(x int, y int) int { return x + y}func sub(x, y int) int { return x - y}func main() { fmt.Println(add(5, 4)) fmt.Println(sub(5, 4))}

In the code example, we have two functions: add and sub.In case of the sub function, the type was omitted for thex variable.

Go function named return variables

We can specify named return variables in round brackets after the functionparameters.

named.go

package mainimport "fmt"func inc(x, y, z int) (a, b, c int) { a = x + 1 b = y + 1 c = z + 1 return}func main() { x, y, z := inc(10, 100, 1000) fmt.Println(x, y, z)}

In the code example, we have a function which increments its three parameters.

func inc(x, y, z int) (a, b, c int) {

We have three named return values: a, b, and c.

a = x + 1b = y + 1c = z + 1return

We compute the values for the return variables. After that, we must specify thereturn keyword.

$ go run namedretvals.go 11 101 1001

Go function multiple return values

Go functions allow to return multiple values.

multiple.go

package mainimport ( "fmt" "math/rand" "time")func threerandom() (int, int, int) { rand.Seed(time.Now().UnixNano()) x := rand.Intn(10) y := rand.Intn(10) z := rand.Intn(10) return x, y, z}func main() { r1, r2, r3 := threerandom() fmt.Println(r1, r2, r3)}

In the code example, we have a threerandom function, which returnsthree random values.

func threerandom() (int, int, int) {

We specify that the function returns three integer values.

rand.Seed(time.Now().UnixNano())x := rand.Intn(10)y := rand.Intn(10)z := rand.Intn(10)

We compute three random values.

return x, y, z

The values are returned from the function. They are separated with commadcharacter.

$ go run multiple.go 0 8 0

Go anonymous function

We can create anonymous functions. Anonymous functions do not have a name.

anonymous.go

package mainimport "fmt"func main() { sum := func(a, b, c int) int { return a + b + c }(3, 5, 7) fmt.Println("5+3+7 =", sum)}

We create an anonymous function which adds three values. We pass three parametersto the function right after its definition.

$ go run anonymous.go 5+3+7 = 15

Go variadic function

A variadic function can accept variable number of parameters. For instance, whenwe want to calculate the sum of values, we might have four, five, six etc.values to pass to the function.

We use the ... (ellipses) operator to define a variadic function.

variadic.go

package mainimport "fmt"func main() { s1 := sum(1, 2, 3) s2 := sum(1, 2, 3, 4) s3 := sum(1, 2, 3, 4, 5) fmt.Println(s1, s2, s3)}func sum(nums ...int) int { res := 0 for _, n := range nums { res += n } return res}

In the code example, we have a sum function which accepts variablenumber of parameters.

func sum(nums ...int) int { res := 0 for _, n := range nums { res += n } return res}

The nums variable is a slice, which contains all values passedto the sum function. We loop over the slice and calculate thesum of the parameters.

$ go run variadic.go 6 10 15

Go recursive function

Recursion, in mathematics and computer science, is a way of defining methods inwhich the method being defined is applied within its own definition. To put itdifferently, a recursive method calls itself to do its task. Recursion is awidely used approach to solve many programming tasks.

A typical example is the calculation of a factorial.

factorial.go

package mainimport "fmt"func fact(n int) int { if n == 0 || n == 1 { return 1 } return n * fact(n-1)}func main() { fmt.Println(fact(7)) fmt.Println(fact(10)) fmt.Println(fact(15))}

In this code example, we calculate the factorial of three numbers.

return n * fact(n-1)

Inside the body of the fact function, we call the factfunction with a modified argument. The function calls itself.

$ go run factorial.go 504036288001307674368000

These are the computed factorials.

Go defer function call

The defer statement defers the execution of a function until thesurrounding function returns. The deferred call's arguments are evaluatedimmediately, but the function call is not executed until the surroundingfunction returns.

defercall.go

package mainimport "fmt"func main() { fmt.Println("begin main") defer sayHello() fmt.Println("end main")}func sayHello() { fmt.Println("hello")}

In the code example, the sayHello function is called after themain function finishes.

$ go run defercall.go begin mainend mainhello

Go pass parameters by value

In Go, parameters to functions are passed only by value.

Note: everything is passed by value in Go. When we passa pointer to a type, a separate copy of that pointer is created. This isdifferent from C, where in case of pointers, the same pointer is passed tothe function.

In the following example, an integer and a User structure arepassed as parameters to functions.

passbyval.go

package mainimport "fmt"type User struct { name string occupation string}func main() { x := 10 fmt.Printf("inside main %d\n", x) inc(x) fmt.Printf("inside main %d\n", x) fmt.Println("---------------------") u := User{"John Doe", "gardener"} fmt.Printf("inside main %v\n", u) change(u) fmt.Printf("inside main %v\n", u)}func inc(x int) { x++ fmt.Printf("inside inc %d\n", x)}func change(u User) { u.occupation = "driver" fmt.Printf("inside change %v\n", u)}

In the code example, the original values of the x and Userstruct are not modified.

func inc(x int) { x++ fmt.Printf("inside inc %d\n", x)}

A copy of the integer value is created. Inside the function, we increment thevalue of this copy. So the original variable is intact.

$ go run callbyval.go inside main 10inside inc 11inside main 10---------------------inside main {John Doe gardener}inside change {John Doe driver}inside main {John Doe gardener}

In the next example, we pass pointers to the the integer variable andstructure.

passbyval2.go

package mainimport "fmt"type User struct { name string occupation string}func main() { x := 10 fmt.Printf("inside main %d\n", x) inc(&x) fmt.Printf("inside main %d\n", x) fmt.Println("---------------------") u := User{"John Doe", "gardener"} fmt.Printf("inside main %v\n", u) change(&u) fmt.Printf("inside main %v\n", u)}func inc(x *int) { (*x)++ fmt.Printf("inside inc %d\n", *x)}func change(u *User) { u.occupation = "driver" fmt.Printf("inside change %v\n", *u)}

Now the original values are modified. But technically, parameters are stillpassed by value. Go creates new copies of the pointers. (This is a differentfrom C.)

inc(&x)

With the & character, we pass a pointer to the xvariable.

func inc(x *int) { (*x)++ fmt.Printf("inside inc %d\n", *x)}

A copy of the pointer to the x variable is created.Changing the value of the x modifies the original variable as well.

$ go run callbyval2.go inside main 10inside inc 11inside main 11---------------------inside main {John Doe gardener}inside change {John Doe driver}inside main {John Doe driver}

The original values have been modified.

Arrays are value types, slices and maps are reference types. So in case of theslices and maps, a copy of the reference is created.

passslice.go

package mainimport "fmt"func main() { vals := []int{1, 2, 3, 4, 5} fmt.Printf("%v\n", vals) square(vals) fmt.Printf("%v\n", vals)}func square(vals []int) { for i, val := range vals { vals[i] = val * val }}

In the code example, we pass a slice to the square function. Theelements of the original slice are modified.

$ go run passslice.go [1 2 3 4 5][1 4 9 16 25]

The elements are squared.

Arrays are value types.

passarray.go

package mainimport "fmt"func main() { vals := [5]int{1, 2, 3, 4, 5} fmt.Printf("%v\n", vals) square(vals) fmt.Printf("%v\n", vals)}func square(vals [5]int) { for i, val := range vals { vals[i] = val * val }}

The example passes an array to the square function.

$ go run passarray.go [1 2 3 4 5][1 2 3 4 5]

The elements are not modified.

Maps are reference types.

passmap.go

package mainimport "fmt"func main() { items := map[string]int{"coins": 1, "pens": 2, "chairs": 4} fmt.Printf("%v\n", items) update(items) fmt.Printf("%v\n", items)}func update(items map[string]int) { items["coins"] = 6}

The example passes a map to the update function.

$ go run passmap.go map[chairs:4 coins:1 pens:2]map[chairs:4 coins:6 pens:2]

As we can see, the original map has been updated.

Go function as parameter

A Go function can be passed to other functions as a parameter. Such a functionis called a higher-order function.

funaspar.go

package mainimport "fmt"func inc(x int) int { x++ return x}func dec(x int) int { x-- return x}func apply(x int, f func(int) int) int { r := f(x) return r}func main() { r1 := apply(3, inc) r2 := apply(2, dec) fmt.Println(r1) fmt.Println(r2)}

In the code example, the apply function takes the inc anddec functions as parameters.

func apply(x int, f func(int) int) int {

We specify that the second parameter is a function type.

r1 := apply(3, inc)r2 := apply(2, dec)

We pass the inc and dec functions to the applyfunction as parameters.

$ go run funaspar.go 41

Go custom function types

Go allows to create reusable functions signatures with the typekeyword.

funtype.go

package mainimport "fmt"type output func(string) stringfunc hello(name string) string { return fmt.Sprintf("hello %s", name)}func main() { var f output f = hello fmt.Println(f("Peter"))}

With the type keyword, we create a function type which acceptsone string parameter and returns a string.

Go filter function

We have a practical example where we filter data.

filtering.go

package mainimport "fmt"type User struct { name string occupation string married bool}func main() { u1 := User{"John Doe", "gardener", false} u2 := User{"Richard Roe", "driver", true} u3 := User{"Bob Martin", "teacher", true} u4 := User{"Lucy Smith", "accountant", false} u5 := User{"James Brown", "teacher", true} users := []User{u1, u2, u3, u4, u5} married := filter(users, func(u User) bool { if u.married == true { return true } return false }) teachers := filter(users, func(u User) bool { if u.occupation == "teacher" { return true } return false }) fmt.Println("Married:") fmt.Printf("%v\n", married) fmt.Println("Teachers:") fmt.Printf("%v\n", teachers)}func filter(s []User, f func(User) bool) []User { var res []User for _, v := range s { if f(v) == true { res = append(res, v) } } return res}

We have a slice of User structures. We filter the slice toform new slices of married users and users that are teachers.

married := filter(users, func(u User) bool { if u.married == true { return true } return false})

We call the filter function. It accepts an anonymous function as aparameter. The function returns true for married users. A functionthat returns a boolean value is known also as a predicate.

func filter(s []User, f func(User) bool) []User { var res []User for _, v := range s { if f(v) == true { res = append(res, v) } } return res}

The filter function forms a new slice for all users that satisfythe given condition.

$ go run filtering.go Married:[{Richard Roe driver true} {Bob Martin teacher true} {James Brown teacher true}]Teachers:[{Bob Martin teacher true} {James Brown teacher true}]

Go closure

A closure in Go is an anonymous function returned from an enclosing function.Closure retains a reference to a variable defined outside its body.

closure.go

package mainimport "fmt"func intSeq() func() int { i := 0 return func() int { i++ return i }}func main() { nextInt := intSeq() fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Println(nextInt()) nextInt2 := intSeq() fmt.Println(nextInt2())}

We have the intSeq function, which generates a sequence of integers.It returns a closure which increments the i variable.

func intSeq() func() int {

The intSeq is a function which returns a function which retrunsan integer.

func intSeq() func() int { i := 0 return func() int { i++ return i }}

Variables defined in functions have a local function scope. However, in this case,the closure is bound to the i variable even after the intSeqfunction returns.

nextInt := intSeq()

We call the intSeq function. It returns a function which willincrement a counter. The closure is stored in the nextInt variable.

fmt.Println(nextInt())fmt.Println(nextInt())fmt.Println(nextInt())fmt.Println(nextInt())

We call the closure several times.

$ go run closure.go 12341

Go higher-order function

A higher-order function is a function which either accepts a function as aparameter or returns a function.

higherorder.go

package mainimport "fmt"func main() { x := 3 y := 4 add, sub := getAddSub() r1, r2 := apply(x, y, add, sub) fmt.Printf("%d + %d = %d\n", x, y, r1) fmt.Printf("%d - %d = %d\n", x, y, r2)}func apply(x, y int, add func(int, int) int, sub func(int, int) int) (int, int) { r1 := add(x, y) r2 := sub(x, y) return r1, r2}func getAddSub() (func(int, int) int, func(int, int) int) { add := func(x, y int) int { return x + y } sub := func(x, y int) int { return x - y } return add, sub}

In the code example, we have some complex operations with functions. Theapply function takes two functions as parameters. ThegetAddSub function returns two functions.

$ go run higherorder.go 3 + 4 = 73 - 4 = -1

In this article we have covered functions in Golang.

Author

My name is Jan Bodnar and I am a passionate programmer with many years ofprogramming experience. I have been writing programming articles since 2007. Sofar, I have written over 1400 articles and 8 e-books. I have over eight years ofexperience in teaching programming.

List all Go tutorials.

Top Articles
Latest Posts
Article information

Author: Laurine Ryan

Last Updated: 08/09/2023

Views: 5753

Rating: 4.7 / 5 (57 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Laurine Ryan

Birthday: 1994-12-23

Address: Suite 751 871 Lissette Throughway, West Kittie, NH 41603

Phone: +2366831109631

Job: Sales Producer

Hobby: Creative writing, Motor sports, Do it yourself, Skateboarding, Coffee roasting, Calligraphy, Stand-up comedy

Introduction: My name is Laurine Ryan, I am a adorable, fair, graceful, spotless, gorgeous, homely, cooperative person who loves writing and wants to share my knowledge and understanding with you.