• 周五. 11 月 13th, 2026

Go语言函数

由root

10 月 13, 2019 #Golang
函数只能判断其是否为nil,不支持其他比较操作
func a() {}
func b() {}
func main() {
    println(a == nil)
    println(b == a) //无效操作
}
从函数返回局部变量指针是安全的,编译器通过逃逸分析(escape analysis)来决定是否在堆上分配内存
func test() *int {
    a := 0x100
    return &a
}
func main() {
    var a *int = test()
    println(a, *a)
}

4.2 参数

不管是指针,引用类型,还是其他类型参数,都是值拷贝传递。区别无非是拷贝目标对象,还是拷贝指针而已。
在函数调用前,会为形参和返回值分配内存空间。
变参本质上就是一个切片,只能接收一到多个同类型参数。且必须放在列表尾部。
func test(s string, a ...int) {
    fmt.Printf("%T,%v\n", a, a)
}
func main() {
    test("abc", 1, 2, 3, 4)
}
将切片作为变参时,须进行展开操作。
func test(a ...int) {
    fmt.Println(a)
}
func main() {
    a := [3]int{10, 20, 30}
    test(a[:]...)   //转换为slice后展开
}

4.3 返回值

有返回值的函数,必须有明确的return终止语句, 除非有panic,或者无break的死循环,则无须return终止语句

4.4 匿名函数

直接执行
func main() {
    func(s string) {
        println(s)
    }("hello world!")
}
赋值给变量
func main() {
    add := func(x, y int) int {
        return x + y
    }
    println(add(1,2))
}
作为参数
func test(f func()) {
    f()
}
func main() {
    test(func(){
      println("hello, world!")  
    })
}
作为返回值
func test() func(int, int) int {
    return func(x, y int) int {
        return x + y
    }
}
func main() {
    add := test()
    println(add(1,2))
}
普通函数和匿名函数都可作为结构体字段,或经通道传递
func testStruct() {
    type calc struct {
        mul func(x, y int) int
    }
    x := calc{
        mul:func(x, y int) int {
            return x * y
        },
    }
    println(x.mul(2,3))
}
func testChannel() {
    c := make(chan func(int,int) int, 2)
    c <- func(x, y int) int {
        return x + y
    }
    println((<-c)(1,2))
}
不曾使用的匿名函数会被编译器当作错误

4.5 延迟调用

语句defer向当前函数注册稍后执行的函数调用。这些调用被称作延迟调用,因为它们直到当前函数执行线束才被执行。常用于资源释放,解除锁定,以及错误处理等操作
多个延迟注册按FILO次序执行
func main() {
    defer println("a")
    defer println("b")
}
/*
b
a
*/
编译器通过插入额外的指令来实现延迟调用执行,而return和panic语句都会终止当前函数流程,引发延迟调用。
相比直接用CALL汇编指令调用函数,延迟调用则须花费更大的代价。这其中包括注册、调用等操作。还有额外的缓存开销。

4.6 错误处理

官方推荐的标准做法是返回error状态
func Scanln(a ...interface{}) (n int, err error)
标准库将error定义为接口类型,以便实现自定义错误类型
type error interface {
    Error() string
}
按惯例,error总是最后一个返回参数。
var errDivByZero = errors.New("division by zero")

func div(x, y int) (int, error) {
    if y == 0 {
        return 0, errDivByZero
    }
    return x / y, nil
}
func main() {
    z, err := div(5, 0)
    if err == errDivByZero {
        log.Fatalln(err)
    }
    println(z)
}
与errors.New类似的还有fmt.Errorf,它返回一个格式化内容的错误对象
自定义错误
type DivError struct {  //自定义错误类型
    x, y int
}
func (DivError) Error() string {    //实现error接口方法
    return "division by zero"
}
func div(x, y int) (int, error) {
    if y == 0 {
        return 0, DivError(x, y)    //返回自定义错误类型
    }
    return x / y, nil
}
func main() {
    z, err := div(5, 0)
    
    if err != nil {
        switch e := err.(type) {        //根据类型匹配
            case DivError:
                fmt.Println(e, e.x, e.y)
            default:
                fmt.Println(e)
        }
        log.Fatalln(err)
    }
    println(z)
}
panic,recover 接近try/catch结构化异常 内置函数,非语句
func panic(v interface{})
func recover() interface{}
panic会立即中断当前函数流程,执行延迟调用。在延迟调用函数中,recover可捕获并返回panic提交的错误对象
package main

import "log"

func main() {
	defer func() {
		if err := recover(); err != nil {	//捕获错误
			log.Fatalln(err)
		}
	}()

	panic("i an dead")	//引发错误
	println("exit.")	//永不会执行
}

连续调用panic 仅最后一个会被recover捕获, recover必须在延迟调用函数中执行才能正常工作

由 root

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注