search.png
关于我
menu.png
go 函数

go 函数声明

go 的函数声明规则和类C语言有较大的不同,它的返回值写在最后。其实这样会更符合人类的思维方式,先想到函数名称、再想到函数需要传的参数,最后才想到函数的返回值:


func testFunc1(name string) (string, error) {
    if name == "" {
        return "", errors.New("name is empty")
    } else {
        return fmt.Sprintf("hello, %s!", name), nil
    }
}

msg,err := testFunc1("")
fmt.Println(msg, err)
msg, err = testFunc1("momo")
fmt.Println(msg, err)

并且go的函数有以下特定:

  1. 支持多返回值
  2. 支持返回值命名
    例如,上例可以写做:
func testFunc2(name string) (msg string, err error) {
    if name == "" {
        msg, err = "", errors.New("name is empty")
    } else {
        msg, err = fmt.Sprintf("hello, %s!", name), nil
    }
    return 
}

msg,err := testFunc2("")
fmt.Println(msg, err)
msg, err = testFunc2("momo")
fmt.Println(msg, err)
  1. 支持匿名函数和闭包。
    此例中testFunc3函数返回一个匿名函数,并且这个匿名函数中包含了闭包变量name。
    在后续运行func1()、func2()时仍然可以正确取到name的值。而正常来说作为形参,离开了函数作用域其值也就不可用了。但是在闭包中,其仍然可以正确取到值。

所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

闭包(Closure),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

func testFunc3(name string) func() {
    return func() {
        var msg string
        var err error
        if name == "" {
            msg, err = "", errors.New("name is empty")
        } else {
            msg, err = fmt.Sprintf("hello, %s!", name), nil
        }
        fmt.Println(msg, err)
    }
}


func1 := testFunc3("")
func2 := testFunc3("momo")
func1()
func2()

执行结果:

 name is empty
hello, momo! <nil>

  1. 函数也是一种类型,和其它任何类型拥有同样的地位,可以作为参数、返回值,赋值给变量、map、slice、数组、channel都可以。
    例如:
type formatFunc func(string) (string, error)
func testFunc4(name string, format formatFunc) {
    msg, err := format(name)
    if err == nil {
        fmt.Println(msg)
    } else {
        fmt.Println("err: " + err.Error())
    }
}


testFunc4("", testFunc1)
testFunc4("momo", testFunc1)

下例使用函数作为变量推送给管道:

// 接受一个推送函数的管道作为参数
func testFunc7(name string, ch chan func(string)) {
    for f := range ch {
        f(name)
    }
}

ch := make(chan func(string))
go func() {
    // 每隔一秒推送一个新的函数
    for i := 0; i < 10; i++ {
        f := func(name string) {
            fmt.Printf("func-%d say: hello %s\n", i, name)
        }
        ch <- f
        time.Sleep(1*time.Second)
    }
    close(ch)
}()

testFunc7("momo", ch)
  1. 不支持 重载 (overload) ,函数的名字不能相同
  2. 不支持 默认参数 (default parameter)。
  3. 当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。
  4. go 语言支持不定参数:args...
// 使用...声明不定参数
func testFunc5(nums ...int) int {
    sum := 0
    for _,v := range nums {
        sum += v
    }
    return sum
}

fmt.Println(testFunc5(1,2,3,4))
a := []int {1,2,3,4}
fmt.Println(testFunc5(a...))
  1. 命名返回参数可以在闭包中读取
    下例中defer是return前最后执行的,仍然可以读取或者修改命名返回参数的值,因为defer是返回前最后执行的,所以输出的是203而不是3.
func testFunc6(x, y int) (z int) {
    defer func() {
        println(z) // 输出: 203
    }()

    z = x + y
    return z + 200 // 执行顺序: (z = z + 200) -> (call defer) -> (return)
}
    
testFunc6(1,2)

版权声明

知识共享许可协议 本文章由作者“衡于墨”创作,转载请注明出处,未经允许禁止用于商业用途

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
发布时间:2021年03月10日 12:21:33

评论区#

还没有评论哦,期待您的评论!

关闭特效