GoLang Functions

📢 This article was translated by gemini-3-flash-preview

GoLang Series

Hello GoLang: https://blog.yexca.net/archives/154
GoLang (var and const) Variables and Constants: https://blog.yexca.net/archives/155
GoLang (func) Functions: This article
GoLang (slice and map) Slices: https://blog.yexca.net/archives/160
GoLang (OOP) Object-Oriented: https://blog.yexca.net/archives/162
GoLang (reflect) Reflection: https://blog.yexca.net/archives/204
GoLang (struct tag) Struct Tags: https://blog.yexca.net/archives/205
GoLang (goroutine) Goroutines: https://blog.yexca.net/archives/206
GoLang (channel) Channels: https://blog.yexca.net/archives/207

Multiple Return Values

Go functions can return multiple values.

Anonymous Return

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

func swap(a, b int) (int, int) {
    return b, a
}

func main() {
    var x, y = swap(10, 20)
    fmt.Println(x, y)
}

/* Output
 * 20 10
 */

Named Parameter Return

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import "fmt"

func swap(a, b int) (x int, y int) {
    x = b
    y = a
    return
}

func main() {
    var x, y = swap(10, 20)
    fmt.Println(x, y)
}

If the return types are the same, they can be merged:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "fmt"

func swap(a, b int) (x, y int) {
    x = b
    y = a
    // x and y default to 0 if not assigned
    return
}

func main() {
    var x, y = swap(10, 20)
    fmt.Println(x, y)
}

init and main

The init function can exist in any package and appear multiple times in the same package, though one is recommended.

The main function must be in package main, and that package must contain the function.

Both are reserved functions; they take no arguments and return no values.

Go automatically calls init() and main().

Program Execution

Program initialization and execution start from the main package. Even if a package is imported by multiple packages, it is only imported once. Here is the execution order:

image

Example:

Assume the following structure:

1
2
3
4
5
6
hello
  -- InitLib1
    -- lib1.go
  -- InitLib2
    -- lib2.go
  main.go

Contents:

lib1.go

1
2
3
4
5
6
7
package InitLib1

import "fmt"

func init() {
    fmt.Println("lib1 init")
}

lib2.go

1
2
3
4
5
6
7
package InitLib2

import "fmt"

func init() {
    fmt.Println("lib2 init")
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
    "fmt"
    // Using an underscore as an alias allows anonymous importing;
    // without it, importing without usage fails compilation.
    _ "hello/InitLib1"
    _ "hello/InitLib2"
)

func init() {
    fmt.Println("main init")
}

func main() {
    fmt.Println("main")
}

Execution result:

1
2
3
4
lib1 init
lib2 init
main init
main

Now, import Lib2 into Lib1, keeping the main code the same:

lib1.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package InitLib1

import (
    "fmt"
    _ "hello/InitLib2"
)

func init() {
    fmt.Println("lib1 init")
}

Running main results in:

1
2
3
4
lib2 init
lib1 init
main init
main

Notice lib2 only initializes once.

Calling Functions from Other Packages

In the previous example, _ was used as an anonymous alias, meaning you couldn’t call methods from that package.

Add a function to lib1:

lib1.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package InitLib1

import (
    "fmt"
    //_ "hello/InitLib2"
)

// Functions must start with a capital letter to be exported.
func Lib1Test() {
    fmt.Println("lib1 test")
}

func init() {
    fmt.Println("lib1 init")
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
    "fmt"
    // Alias the package
    mylib1 "hello/InitLib1"
    _ "hello/InitLib2"
)

func main() {
    // Call via alias.Method
    mylib1.Lib1Test()
    fmt.Println("main")
}

/*
 * Output
 * lib1 init
 * lib2 init
 * lib1 test
 * main
 */

Alternatively, you can use ., in main.go:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import (
    "fmt"
    // Change alias to .
    . "hello/InitLib1"
    _ "hello/InitLib2"
)

func main() {
    // Use directly
    Lib1Test()
    fmt.Println("main")
}

Not recommended; it creates ambiguity if two packages have functions with the same name.

Pointers

Similar to C pointers.

Parameters can be passed by value or by pointer (reference). By default, Go uses pass-by-value (as seen in the first section of this post).

Use & to get a variable’s memory address.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package main

import (
    "fmt"
)

func main() {
    var a int
    fmt.Printf("%x", &a)
}

Passing by reference sends the memory address to the function; modifications affect the original variable. Here’s the swap function again, using pointers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "fmt"

func swap(a, b *int) {
    var tmp = *a
    *a = *b
    *b = tmp
}

func main() {
    x, y := 10, 20
    swap(&x, &y)
    fmt.Println("x =", x, "y =", y)
}

defer

The defer statement schedules a function call to run just before the surrounding function returns. It is often used for:

  • Releasing resources
  • Catching and handling panics
  • Logging

It’s similar to finally in a try...catch...finally block.

Commonly used for paired operations like opening/closing files, acquiring/releasing locks, or connecting/disconnecting. It ensures resources are cleaned up even if an error occurs or the function returns early.

If a function has multiple defer statements, they are executed in LIFO (Last-In-First-Out) order, like a stack.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func deferDemo() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    defer fmt.Println("4")
}

func main() {
    deferDemo()
}

/*
 * Output
 * 4
 * 3
 * 2
 * 1
 */

recover

A runtime panic will crash the program. recover is a built-in function used to “intercept” a panic, similar to a catch block in Java.

recover is only effective when called inside a deferred function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

func deferDemo(i int) {
    var arr [10]int
    // Set up interceptor before the error occurs
    defer func() {
        // Use recover to catch panic info
        err := recover()
        if err != nil {
            fmt.Println(err)
        }
    }()
    arr[i] = 10
}

func main() {
    deferDemo(10)
    fmt.Println("main code")
}

/*
 * Output
 * runtime error: index out of range [10] with length 10
 * main code
 */