Home GoLand 函数
Post
Cancel

GoLand 函数

多返回值

Go 函数可以返回多个值

匿名返回

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)
}

/* 输出
 * 20 10
 */

有形参名返回

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)
}

上述返回值类型相同,可以合并

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,y 赋值,默认为0
	return
}

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

init 与 main

init 可以在任意包,也可以在同一个包中出现多次,但建议只写一个

main 只能在 package main 中,且该包必须有该函数

这两个函数为保留函数,定义时不能有参数和返回值

Go 程序会自动调用 init() 和 main()

程序执行

程序的初始化和执行都起始于 main 包,同一个包就算被多个包 import 导入也只会导入一次,下图为执行顺序

image

例子

假设结构如下

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

内容如下

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
package main

import (
	"fmt"
    // 此处下划线为包起别名,不起别名,导入不调用编译不通过
	_ "hello/InitLib1"
	_ "hello/InitLib2"
)

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

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

运行结果

1
2
3
4
lib1 init
lib2 init
main init
main

现在将 Lib1 包导入 Lib2,main 代码不变

lib1.go

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

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

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

运行 main 结果

1
2
3
4
lib2 init
lib1 init
main init
main

lib2 只出现一次

调用其他包函数

上例使用 _ 作为别名是匿名的,无法调用相应包的方法

在 lib1 中增加函数,lib1.go

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

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

// 首字母大写才可在其他包调用
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"
    // 给包起别名
	mylib1 "hello/InitLib1"
	_ "hello/InitLib2"
)

func main() {
    // 通过别名.方法调用
	mylib1.Lib1Test()
	fmt.Println("main")
}

/*
 * 输出
 * lib1 init
 * lib2 init
 * lib1 test
 * main
 */

或者可以直接使用 .,文件 main.go

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

import (
	"fmt"
    // 别名改为 .
	. "hello/InitLib1"
	_ "hello/InitLib2"
)

func main() {
    // 直接使用
	Lib1Test()
	fmt.Println("main")
}

不推荐使用,假设俩包有同名函数,将发生歧义

指针

与 C 指针类似

调用函数可以使用两种方式传递参数,值传递与指针 (引用传递) 。默认情况使用值传递,像本文第一段代码即为值传递

使用 & 可以获取变量对应的内存地址

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

import (
	"fmt"
)

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

引用传递将内存地址传递给函数,函数修改将影响实际参数,同样是交换函数,这次使用指针

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

defer 语句用于预定对一个函数的调用,可以称为延迟函数,作用:

  • 释放占用的资源
  • 捕捉处理异常
  • 输出日志

类似于 try…catch…finally 的 finally

常用于处理成对的操作,如打开/关闭文件、获取/释放锁、连接/断开连接等,确保资源被适当地释放,即使在发生错误或提前返回的情况下也能保证执行

如果一个函数中有多个 defer 语句,类似于栈,以 LIFO(后进先出) 顺序执行

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()
}

/*
 * 输出
 * 4
 * 3
 * 2
 * 1
 */

recover

运行时 panic 异常一旦被引发就会导致程序崩溃,recover 为用于 “拦截” 运行时 panic 的内建函数,类似 Java 的 try…catch 的抓取异常

recover 只有在 defer 调用的函数中有效

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
	// 错误拦截在错误前设置
	defer func() {
		// 设置recover拦截错误信息
		err := recover()
		if err != nil {
			fmt.Println(err)
		}
	}()
	arr[i] = 10
}

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

/*
 * 输出
 * runtime error: index out of range [10] with length 10
 * main code
 */
This post is licensed under CC BY 4.0 by the author.

GoLand 变量与常量

Redis