美文网首页
defer、return及赋值顺序

defer、return及赋值顺序

作者: luckiexie | 来源:发表于2019-04-19 22:23 被阅读0次

    在使用go语言开发项目时,有这么个需求:在函数返回前检查某个值是否合法,如果不合法则进行修正。那最自然的实现方式如下:

    func getNumber() int {
        number := 0
        threshold := 10000
        // do something and assign value to number
        number = 99999
        // do something
        // ... ...
    
        // check number is valid or not
        if number > threshold {
            number = threshold
        }
        return number
    }
    

    但是这种方式有个弊端,当number赋值为99999后,在check前,如果函数返回了,则会漏掉检查,得到不符合预期的值。使用java语言解决这个问题的方法是在finally块中进行检查动作,则不会漏掉检查的逻辑。同样,在golang中也提供了类似的方法---使用defer关键字。defer会在函数返回前执行。利用这一特性,我们可以这样实现:

    func getNumber() int {
        threshold := 10000
        number := 0
        
        // check before return
        defer func() {
            // check number is valid or not
            if number > threshold {
                number = threshold
            }
        }()
        
        // do something and assign value to number
        number = 99999
        // do something
        // ... ...
        
        return number
    }
    
    func main() {
        fmt.Println(getNumber())
    }
    

    main函数里的输出可能有点意外,输出为99999。并没有得到我们预期的结果。那这里的原因是什么呢?其实,return包含了两个步骤:

    1. 给返回值赋值;
    2. 调用RET返回指令并传入返回值,而RET则会检查defer是否存在,若存在就先逆序插播defer语句,最后RET携带返回值退出函数。

    这里需要理解下第一步,给返回值赋值时,如果是匿名返回值,则会申明一个变量,并将值赋值给该变量。第二步再将该变量传入RET。因此,对于匿名返回值的情况,最终返回值是在defer执行前就已经确定,即使defer执行修改了number,但是并不会修改返回值的变量。那对于具名返回值,则在return赋值时,是直接对具名变量赋值(因为在函数申明时,返回值变量就已存在),defer修改返回值会生效。如下:

    func getNumber() (number int) {
        threshold := 10000
        
        // check before return
        defer func() {
            // check number is valid or not
            if number > threshold {
                number = threshold
            }
        }()
    
        // do something and assign value to number
        number = 99999
        // do something
        // ... ...
    
        return number
    }
    

    这样实现,便能达到我们想要的效果。因此,在需要在函数返回前修改返回值的情况下,可以考虑使用具名返回值的形式,以防踩坑。

    相关文章

      网友评论

          本文标题:defer、return及赋值顺序

          本文链接:https://www.haomeiwen.com/subject/pfhhgqtx.html