Go 快速入门指南 - defer

知乎大V
• 阅读 938

概述

一个 defer 语句就是一个普通的函数或方法调用。 defer 语句保证了不论是在正常情况下 (return 返回),
还是非正常情况下 (发生错误, 程序终止),函数或方法都能够执行。

主要特性

  • 一个函数可定义多个 defer 语句
  • defer 表达式中的变量值在 defer 表达式定义时已经确定
  • defer 表达式可以修改函数中的命名返回值

主要作用

  • 简化异常处理 ( 使用 defer + recover),避免异常与控制流混合在一起 (try … catch … finally)
  • defer 做资源释放和配置重置等收尾工作

语法规则

如果 defer 函数只有一行语句,可以省略 func() { ... } 代码块,否则就需要用 func() { ... } 代码块包起来。

多个 defer 执行顺序

如果一个函数中注册了多个 defer 函数,这些函数会按照 后进先出 的顺序执行 (和 的出栈顺序一致)。
也就是最后注册的 defer 函数会第一个执行,而第一个注册的 defer 函数会最后执行。

例子

函数退出前打印字符

package main

func A() {
    defer println("A 函数执行完成")

    println("A 函数开始执行")
}

func B() {
    defer println("B 函数执行完成")

    println("B 函数开始执行")
}

func main() {
    A()
    B()
}

// $ go run main.go
// 输出如下 
/**
  A 函数开始执行
  A 函数执行完成
  B 函数开始执行
  B 函数执行完成
*/

关闭文件句柄

package main

import (
    "fmt"
    "os"
)

func createFile(name string) *os.File {
    file, err := os.Create(name)
    if err != nil {
        panic(err)
    }
    return file
}

func writeFile(file *os.File) {
    n, err := file.WriteString("hello world")
    if err != nil {
        panic(err)
    } else {
        fmt.Printf("成功写入 %d 个字符\n", n)
    }
}

func closeFile(file *os.File) {
    err := file.Close()
    if err != nil {
        panic(err)
    }
}

func main() {
    file := createFile("/tmp/defer_test.txt")
    defer closeFile(file) // 获取到文件句柄后,第一时间注册 defer 函数

    writeFile(file)
}

// $ go run main.go
// 输出如下 
/**
  成功写入 11 个字符
*/

// $ cat /tmp/defer_test.txt
// 输出如下
/**
  hello world
*/

多个 defer 函数

package main

func A() {
    defer println("第 1 个 defer 函数")

    defer func() { // 这里为了演示 func() { ... } 的语法
        defer println("第 2 个 defer 函数")
    }()

    defer println("第 3 个 defer 函数")

    println("A 函数开始执行")
}

func main() {
    A()
}

// $ go run main.go
// 输出如下
/**
  A 函数开始执行
  第 3 个 defer 函数
  第 2 个 defer 函数
  第 1 个 defer 函数
*/

reference

  1. Go 圣经

联系我

Go 快速入门指南 - defer

点赞
收藏
评论区
推荐文章
Stella981 Stella981
3年前
Python—执行系统命令的四种方法(os.system、os.popen、commands、subprocess)
一、os.system方法这个方法是直接调用标准C的system()函数,仅仅在一个子终端运行系统命令,而不能获取命令执行后的返回信息。os.system(cmd)的返回值。如果执行成功,那么会返回0,表示命令执行成功。否则,则是执行错误。使用os.system返回值是脚本的退出状态码,该方法在调用完shell脚本后
Wesley13 Wesley13
3年前
SQL中的函数
概括:函数是由一个或多个TSQL语句组成的子程序,是一组可用于封闭实现一定功能的程序代码,函数使代码便于重复使用。类别:1、聚合函数聚合函数对一个组值执行计算,并返回单个值。除了COUNT以外,聚合函数都会忽略空置。聚合函数经常与SELECT语句的GROUPBY字句一起使用。常用的聚合函数包括AVC、COUNT、MA
peter peter
4年前
深入剖析 defer 原理篇 —— 函数调用的原理?
本篇文章是深入剖析golang的defer的基础知识准备,如果要完全理解defer,避免踩坑,这个章节的基础知识必不可少。我们先复习一个最基础的知识——函数调用。这个对理解defer在函数里的行为必不可少。那么,当你看到一个函数调用的语句你能回忆起多少知识点呢?地址空间下图是一个典型的操作系统的地址空间示意图:(h
科工人 科工人
4年前
Go语言学习——彻底弄懂return和defer的微妙关系
疑问前面在函数篇里介绍了Go语言的函数是支持多返回值的。只要在函数体内,对返回值赋值,最后加上return就可以返回所有的返回值。最近在写代码的时候经常遇到在return后,还要在defer里面做一些收尾工作,比如事务的提交或回滚。所以想弄清楚这个return和defer到底是什么关系,它们谁先谁后,对于最后返回值又有什么影响呢?动手验证了解
亚瑟 亚瑟
4年前
Hook 规则 – React
Hook规则_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。Hook本质就是JavaScript函数,但是在使用它时需要遵循两条规则。我们提供了一个来强制执行这些规则:只在最顶层使用Hook不要在循环,条件或嵌套函数中调用Hoo
Wesley13 Wesley13
3年前
Oracle基于布尔的盲注总结
0x01decode 函数布尔盲注decode(字段或字段的运算,值1,值2,值3)这个函数运行的结果是,当字段或字段的运算的值等于值1时,该函数返回值2,否则返回3当然值1,值2,值3也可以是表达式,这个函数使得某些sql语句简单了许多使用方法:比较大小select
Wesley13 Wesley13
3年前
C语言
主函数程序的入口main函数不可没有,也不可出现多行main函数,main有且仅有一个。intmain()return0main前面的int指的是函数调用后返回一个整型值。int是整形。返回0,0是整数voidmain是过时的函数。库函数C语言本身提供给我们使用的函数。数据类型:char字符型
Stella981 Stella981
3年前
Python3——匿名函数
没有名字的函数,不用写return,返回值就是该表达式的结果。语法:lambda参数:方法(或三元运算)lambdax:xx等同于deff(x):returnxx普通函数defcalc(x,y):ifxy:retur
Wesley13 Wesley13
3年前
C库函数、系统函数等调用错误的处理方法
几乎所有的系统函数和库函数在执行时都会通过返回特定的值来说明成功或出错。我们在调用它们后,必须马上对其返回值进行检测,如果调用出错则要进行相应的处理(一般是向终端输出错误信息并终止程序运行)。否则在今后程序出错时,如果通过调试去定位到该错误将会花费很长的时间。当然也有某些系统调用从不失败(例如getpid()或\_exit()等),在调用它们时可以不
Wesley13 Wesley13
3年前
Java 异常处理
完善的异常处理有利于程序稳定。不要不停的catch异常。什么是异常??定义:异常是一个事件,它发生在程序的执行过程中,会破坏程序的正常执行在一个错误发生会在一个方法时,创建一个Exception对象来处理来保证程序能继续执行下去。当异常发生时,JVM会搜索调用栈上的所有方法,若没有找到合适的异常处理方法,JVM将会终
小万哥 小万哥
1年前
Java break、continue 详解与数组深入解析:单维数组和多维数组详细教程
JavaBreak和ContinueJavaBreak:break语句用于跳出循环或switch语句。在循环中使用break语句可以立即终止循环,并继续执行循环后面的代码。在switch语句中使用break语句可以跳出当前case,并继续执行下一个case