开发者

Go语言defer的一些神奇规则示例详解

开发者 https://www.devze.com 2022-12-04 11:14 出处:网络 作者: 程序员麻辣烫
目录测试题分析规则一当defer被声明时,其参数就会被实时解析规则二 defer可能操作主函数的具名返回值规则三 延迟函数执行按后进先出顺序执行坑实例测试题
目录
  • 测试题
  • 分析
    • 规则一当defer被声明时,其参数就会被实时解析
    • 规则二 defer可能操作主函数的具名返回值
    • 规则三 延迟函数执行按后进先出顺序执行
  • 坑实例

    测试题

    defer有一些规则,如果不了解,代码实现的最终结果会与预期不一致。对于这些规则,你了解吗?

    这是关于defer使用的代http://www.devze.com码,可以先考虑一下返回值。

    package main
    import (
    	"fmt"
    )
    /**
     * @Author: Jason Pang
     * @Description: 快照
     */
    func deferFuncParameter1() {
    	var aInt = 1
    	defer fmt.Println(aInt)
    	aInt = 2
    	return
    }
    /**
     * @Author: Jason Pang
     * @Description: 快照
     */
    func deferFuncParameter2() {
    	var aInt = 1
    	defer func(t int) {
    		fmt.Println(t)
    	}(aInt)
    	aInt = 2
    	return
    }
    /**
     * @Author: Jason Pang
     * @Description: 动态
     */
    func deferphpFuncParameter3() {
    	var aInt = 1
    	defer func() {
    		fmt.Println(aInt)
    	}()
    	aInt = 2
    	return
    }
    /**
     * @Author: Jason Pang
     * @Description: 影响返回值
     * @return ret
     */
    func deferFuncReturn1() (ret int) {
    	ret = 10
    	defer func() {
    		ret++
    	编程客栈	fmt.Println("-----", ret)
    	}()
    	return 2
    }
    /**
     * @Author: Jason Pang
     * @Description: 不影响返回值
     * @return ret
     */
    func deferFuncRandroideturn2() (ret int) {
    	ret = 10
    	defer func(ret int) {
    		ret++
    		fmt.Println("-----", ret)
    	}(ret)
    	return 2
    }
    /**
     * @Author: Jason Pang
     * @Description: defer顺序
     */
    func deferFuncSeq1() {
    	var aInt = 1
    	defer fmt.Println(aInt)
    	aInt = 2
    	defer fmt.Println(aInt)
    	return
    }
    func main() {
    	fmt.Println("快照")
    	deferFuncParameter1()
    	deferFuncParameter2()
    	deferFuncParameter3()
    	fmt.Println("返回值")
    	fmt.Println(deferFuncReturn1())
    	fmt.Println(deferFuncReturn2())
    	fmt.Println("执行顺序")
    	deferFuncSeq1()
    }
    

    正确输出为:

    ➜ myproject go run main.go

    快照

    1

    1

    2

    返回值

    ----- 3

    3

    ----- 11

    2

    执行顺序

    2

    1

    分析

    defer有几条重要规则,上面的结果都能从这些规则中找到答案。

    规则一当defer被声明时,其参数就会被实时解析

    当defer被声明的时候,如果直接使用了参数,此时的参数就会使用快照值,在整个生命周期内不会python变化。如deferFuncParameter1、deferFuncParameter2,虽然aInt在defer声明后被变更,但defer里的值不会再变了。

    func deferFuncParameter1() {
    	var aInt = 1
    	defer fmt.Println(aInt)
    	aInt = 2
    	return
    }
    func deferFuncParameter2() {
    	var aInt = 1
    	defer func(t int) {
    		fmt.Println(t)
    	}(aInt)
    	aInt = 2
    	return
    }
    

    与之相反的是deferFuncParameter3,随aInt的变化而变化。

    func deferFuncParameter3() {
    	var aInt = 1
    	defer func() {
    		fmt.Println(aInt)
    	}()
    	aInt = 2
    	return
    }
    

    规则二 defer可能操作主函数的具名返回值

    defer有可能更改函数的返回值,这是最容易导致错误的地方。

    关键字_return_不是一个原子操作,实际上_return_只代理汇编指令_ret_,即将跳转程序执行。比如语句 return i ,实际上分两步进行,即将i值存入栈中作为返回值,然后执行跳转,而defer的执行时机正是跳转前,所以说defer执行时还是有机会操作返回值的。return i的执行过程如下所示:

    result = i 

    执行defer

    return

    所以基于这个规则,对于deferFuncReturn1,

    func deferFuncReturn1() (ret int) {
    	ret = 10
    	defer func() {
    		ret++
    		fmt.Println("-----", ret)
    	}()
    	return 2
    }
    

    执行过程为:

    ret = 2

    ret++

    fmt.Println("-----", ret)

    return

    所以最终ret的值为3。

    对于deferFuncReturn2,因为defer声明的时候直接使用了参数,所以使用的是快照,不会影响ret的返回值。

    规则三 延迟函数执行按后进先出顺序执行

    即先出现的 defer最后执行

    这个规则大家都很熟悉,defer按照栈的顺序执行。

    坑实例

    举一个错误使用defer的实例。在go中使用事务时,有一种推荐写法:将Rollback放到defer中,通过判断函数是否有报错或者panic,来判断是否要回滚。

    func  Update() (resp *baseinfo.Resp, err error) {
    	//开启事务
    	panicked := true
    	tx, err := db.TXBegin()
    	if err != nil {
    		return resp, nil
    	}
    	defer func() 开发者_C教程{
    		if panicked || err != nil {
    			tx.Rollback()
    		}
    	}()
    	//更新
    	err = h.update(shopId, tx)
    	if err != nil {//失败返回
    		return resp, nil
    	}
    	panicked = false
    	err = tx.Commit().Error
    	if err != nil { //失败返回
    		return resp, nil
    	}
    	return
    }
    

    判断回滚的err正是函数的具名返回值,在有报错的情况下,返回值被赋值为nil,这意味如果有失败,Rollback也不会被执行。

    之所以不将err直接返回,而是使用nil,是因为框架设计的问题,业务错误通过resp返回,如果直接返回err,框架会认为是RPC错误。

    以上就是Go语言defer的一些神奇规则示例详解的详细内容,更多关于Go语言defer规则的资料请关注我们其它相关文章!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号