📢 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:

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
*/
|