避免defer陷阱:拆解延迟语句,掌握正确使用方法

基本概念

Go语言的延迟语句defer有哪些特点?通常在什么情况下使用?

Go语言的延迟语句(defer statement)具有以下特点:

  1. 延迟执行:延迟语句会在包含它的函数执行结束前执行,无论函数是正常返回还是发生异常。

  2. 后进先出:如果有多个延迟语句,它们会按照后进先出(LIFO)的顺序执行。也就是说,最后一个延迟语句会最先执行,而第一个延迟语句会最后执行。

通常情况下,延迟语句在以下情况下使用:

  1. 资源释放:延迟语句可以用于在函数返回前释放打开的文件、关闭数据库连接、释放锁等资源,以确保资源的正确释放,避免资源泄漏。

  2. 错误处理:延迟语句可以用于处理函数执行过程中可能发生的错误。通过在函数开始时设置延迟语句,在函数返回前检查错误并进行相应的处理,可以简化错误处理的逻辑。

  3. 日志记录:延迟语句可以用于在函数返回前记录日志或执行其他的调试操作,以便在函数执行过程中收集相关的信息。

延迟语句的使用可以提高代码的可读性和可维护性,同时确保资源的释放和清理操作按照逆序进行。它是Go语言中一种常用的编程技巧,用于处理资源管理和错误处理等场景。

避坑之旅

实际开发中defer的使用并不像前面介绍的这么简单,defer用不好,会陷入泥潭。

下面我从两个角度带大家避坑:

  1. 首先拆解一下延迟语句的执行,注意Go语言的return语句不是原子性的;

  2. 另外重点和大家分享一下defer语句后面使用匿名函数和非匿名函数的区别。

拆解延迟语句

避免陷入泥潭的关键是必须深刻理解下面这条语句:

return xxx

上面这条语句经过编译之后,实际上生成了三条指令:

1)返回值 =xxx。

2)调用 defer 函数。

3)空的 return。

第1和第 3 步是return语句生成的指令,也就是说return并不是一条原子指令;

第2步是 defer 定义的语句,这里可能会操作返回值,从而影响最终结果。

下面来看两个例子,试着将return 语句和 defer语句拆解到正确的顺序。

第一个例子:

func f()(r int){
  t:=5

  defer func(){
    t=t+5
    }()
    
  return t
}

拆解后:

func f()(r int){
  t:=5
  
  //1,赋值指令
  r=t

  // 2.defer 被插入到赋值与返回之间执行,这个例子中返回值r没被修改过 
  func(){
    t=t+5
    }()
    
  //3.空的 return 指令
  return
  }

这里第二步实际上并没有操作返回值r,因此,main函数中调用f()得到5。

第二个例子:

func f()(r int){
  defer func(r int){
    r=r+5
    }(r)
    
    return 1
}

拆解后:

func f() (r int) {
  //1.赋值 
  r=1
  
  //2.这里改的r是之前传进去的r,不会改变要返回的那个r值 
  func(r int) {
    r=r+5
  }(r)
  
  // 3. 空的 return 
  return
}

第二步,改变的是传值进去的r,是形参的一个复制值,不会影响实参r。因此,main函数中需要调用f()得到1。

defer匿名函数

在Go语言中,使用匿名函数作为defer的参数时,可以理解为:defer语句中的匿名函数在包裹该defer语句的函数返回后才执行。这是因为defer语句的执行时机是在包裹函数即将返回之前,但在实际返回之前。

为什么不是在return语句之前执行呢?这是因为defer语句的设计初衷是为了在函数返回之前执行一些清理操作,例如关闭文件、释放资源等。将defer语句放在return语句之后,可以确保在函数返回之前执行这些清理操作,保证函数的执行完整性和资源的正确释放。

在使用匿名函数和非匿名函数作为defer的参数时,主要区别在于对函数参数的传递和作用域的影响:

  1. 匿名函数作为defer的参数:匿名函数可以直接在defer语句中定义,可以访问外部函数的变量,并且在执行时会使用当前的变量值。这种方式可以方便地在defer语句中使用外部变量,但需要注意变量的值在执行时可能已经发生了改变。

  2. 非匿名函数作为defer的参数:非匿名函数需要先定义好,然后作为defer的参数传递。在执行时,会使用函数的当前参数值。这种方式可以在defer语句中使用已定义的函数,但需要注意函数参数的传递和作用域。

产生这种区别的原因是,匿名函数和非匿名函数在定义和作用域上的差异。匿名函数可以直接在defer语句中定义,可以访问外部函数的变量,而非匿名函数需要先定义好,然后作为参数传递。这种设计灵活性使得开发者可以根据具体的需求选择合适的方式来使用defer语句。

举例来说

当使用匿名函数作为defer的参数时,可以在defer语句中直接定义匿名函数,并访问外部变量。

以下是一个示例代码:

package main

import "fmt"

func main() {
    x := 10

    defer func() {
        fmt.Println("Deferred anonymous function:", x)
    }()

    x = 20
    fmt.Println("Before return:", x)
}

在上述示例中,匿名函数作为defer的参数,可以访问外部变量x
在函数返回之前,defer语句中的匿名函数会执行,并打印出x的值。

输出结果如下:

当使用非匿名函数作为defer的参数时,需要先定义好函数,然后将函数名作为defer的参数传递。

以下是一个示例代码:

package main

import "fmt"

func main() {
    x := 10

    defer printX(x)

    x = 20
    fmt.Println("Before return:", x)
}

func printX(x int) {
    fmt.Println("Deferred function:", x)
}

在上述示例中,printX函数作为defer的参数传递,函数定义在main函数之后。

在函数返回之前,defer语句中的printX函数会执行,并打印出传递的参数x的值。输出结果如下:

总结一下

通过以上示例,我们可以明确体现出使用匿名函数和非匿名函数作为defer的参数的区别。

匿名函数可以直接在defer语句中定义,并访问外部变量,而非匿名函数需要先定义好函数,然后将函数名作为参数传递。

通过前面带着大家拆解了defer的语句的执行,相信大家可以更好的理解了。

学Go碰到问题,欢迎加我微信:wangzhongyang1993

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/152289.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年数维杯国际赛赛题思路浅析(快速选题)

2023年数维杯国际赛作为今年下半年第一场数模英文论文竞赛如期开赛。本次赛题的题设,难度开始向2020年之前的国赛看齐。比赛仿照美赛设置了MCM两道,ICM两道。需要注意的是与其他常规数模竞赛不同的是该竞赛支持各参赛队不区分组别,可从4套题中…

医疗软件制造商如何实施静态分析,满足 FDA 医疗器械网络安全验证

随着 FDA 对网络安全验证和标准提出更多要求,医疗软件制造商需要采用静态分析来确保其软件满足这些新的安全标准。继续阅读以了解如何实施静态分析来满足这些安全要求。 随着 FDA 在其软件验证指南中添加更多网络安全要求,医疗设备制造商可以转向静态分…

WorkPlus即时通讯app支持多种信创环境组合运行

在信息技术领域,国产信创技术的快速发展为企业带来了更多的选择和机会。在此背景下,WorkPlus作为一款全方位的移动数字化平台,全面支持国产信创操作系统、芯片和数据库,并且全面兼容鸿蒙操作系统。这一优势使得WorkPlus成为了企业…

如何使用ArcGIS Pro制作粉饰效果

在地图上,如果某个部分比较重要,直接的制图不能将其凸显出来,如果想要突出显示重要部分,可以通过粉饰效果来实现,这里为大家介绍一下方法,希望能对你有所帮助。 数据来源 本教程所使用的数据是从水经微图…

【C++&数据结构】二叉树(结合C++)的经典oj例题 [ 盘点&全面解析 ](24)

前言 大家好吖,欢迎来到 YY 滴数据结构系列 ,热烈欢迎! 本章主要内容面向接触过C的老铁 主要内容含: 欢迎订阅 YY滴 数据结构 专栏!更多干货持续更新!以下是传送门! 目录 一.二叉树创建字符串1…

用封面预测书的价格【图像回归】

今天,我将介绍计算机视觉的深度学习应用,用封面简单地估算一本书的价格。 我没有看到很多关于图像回归的文章,所以我为你们写这篇文章。 距离我上一篇文章已经过去很长时间了,我不得不承认,作为一名数据科学家&#x…

Flowable 定时器事件

# 注意数据库时区的配置,如果差8小时配置成Asia/Shanghai spring.datasource.urljdbc:mysql://localhost:3306/flowable660?serverTimezoneAsia/Shanghai&nullCatalogMeansCurrenttrue# 开启定时任务功能 flowable.async-executor-activate: true一&#xff1a…

android studio编译SDL so库

一、下载源码 SDL官网 二、解压,拷贝android项目,并重新命名 2.1、解压 2.2,重命名项目名称(androidSDL)AndroidSDL Github 三、导入头文件和源文件,修改android.mk文件 3.1、在jni目录下创建SDL2文件…

腾讯云服务器可用区是什么意思?

腾讯云服务器可用区是什么意思?云服务器可用区如何选择?可用区是指在同一个地域内电力和网络相互独立的区域,可用区可以做到故障隔离,所以可用区存在的意义在于构建高可用、高容灾应用,将应用部署在不同可用区内&#…

爬虫基础之爬虫的基本介绍

一、爬虫概述 爬虫又称网络蜘蛛、网络机器人,网络爬虫按照系统结构和实现技术,大致可以分为以下几种类型: 通用网络爬虫(Scalable Web Crawler):抓取互联网上所有数据,爬取对象从一些种子 URL…

腾讯云服务器可用区是什么意思?可用区选择方法

腾讯云服务器可用区是什么意思?云服务器可用区如何选择?可用区是指在同一个地域内电力和网络相互独立的区域,可用区可以做到故障隔离,所以可用区存在的意义在于构建高可用、高容灾应用,将应用部署在不同可用区内&#…

【2024全新版】程序员必会英语词汇表

“我英语不好可以学编程吗?” 相信这个问题,困扰着太多想学习编程,但英文不好的同学。 学习编程,常用的单词就那么多,只要把常见的单词学会,你的代码就能写的很6,英 语和编程的关系就是这么纯…

市场研究报告:量子计算将颠覆银行业!

(图片来源:网络) 量子银行将对金融体系产生重大影响,它在量子计算和区块链的基础上建立了一个更快的支付机制,并且通过消除传统点对点支付中常见的中间人,降低了运营成本。 量子计算及其运作机制 中东地区…

利用ffmpeg实现rtmp和rtsp推流

环境说明 windows11 : ffmpeg VLC Linux Unbuntu20.04 : SRS MediaMTX 可选:GStreamer win11下载ffmpeg和ffplay ffmpeg官网 添加环境变量:添加ffmpeg/bin所在的路径。 D:\ffmpeg\ffmpeg-master-latest-win64-lgpl-shared\bin win11查看本机电脑的设备…

JRebel

JRebel 下载: 1.在idea 直接下载 但版本不好控制 2.仓库下载地址:https://plugins.jetbrains.com/plugin/4441-jrebel-and-xrebel/versions/stable 注意版本:2022 .4.1 激活: 打开地址:https://jrebel.qekang.com/ …

英伟达真是赢麻了,深夜推出最强AI芯片霸场 | 百能云芯

10月14日凌晨,英伟达在2023年全球超算大会(Supercomputing Conference,SC)上正式宣布,升级旗舰AI芯片,推出全新的H200芯片,以处理更强大的人工智能系统。包括亚马逊的AWS、Alphabet的Google Clo…

CDP体系化建设1-CDP综述

前言 从CRM到DMP,再到CDP的横空出世,数据产品领域推陈出新的速度也挺快的。 而了解CDP的人可能会说,CDP和BI一样,糅杂了太多东西,都不知道如何概括。 在我看来,CDP也是一个看似简单,但是需要借助…

MYSQL存储引擎和索引

存储引擎 InnoDB(默认) 存储引擎的对比 MYISAM被MangoDB替代了 MEMORY被Redis替代了 索引 是一种高效获取数据的数据结构 索引结构 二叉树,红黑树(都不合适) B树 插入超过5个数,会从中间分裂 B树 …

Qt高级--(2)自定义标题栏

自定义标题栏 功能点 1.标题栏中最外层布局器使用水平布局器。 2.导航按钮、工具按钮和窗口功能按钮都是用水平布局器,边距和间隔可根据实际情况设置。 3.编写 QSS 样式,并将样式设置到窗口控件中。 4.实现最小化、最大化和关闭窗口按钮功能。 5.实现鼠…

可信联邦学习冬令营·成都开营,产学研共促AI人才培养

近年来,国家对于人工智能和数据安全的重视程度不断加强。国务院《新一代人工智能发展规划》中明确提出了加强人工智能领域的基础研究、培养高素质人才、促进产业融合等方面的要求。联邦学习是人工智能和隐私计算核心技术之一,以“数据不动模型动&#xf…