乐趣区

关于go:Go-defer-去掉闭包函数靠谱吗

大家好,我是煎鱼。

在 Go 语言里,defer 关键字是大家很爱用的。因为他有着 defer+recover+panic 的组合拳打法,还有种各种 defer close 等罕用场景。

defer 常见用法

在语法上,Go defer 的代码示例如下:

package main

import "fmt"

func main() {defer fmt.Println("煎鱼你好!")

    fmt.Println("放学别走")
}

输入后果:

 放学别走
煎鱼你好!

那 defer 在 Go 里的常见用法有哪些呢?首先是上文用到的,间接 defer + 函数:

defer f()

其次是 defer+ 闭包的形式:

defer func() {result := f()
   // do something with result
}()

其余还有在面试题上常被讲究的传参变形:

func f1() int {
    i := 1
    defer func() {i++}()
    ...
}
func f2() int {
    i := 1
    defer func(i int) {i++}(i)
    ....
}

这些代码看起来,咱们总是在对 defer 做闭包的各种申明和应用。defer 会不会就是和闭包天生一对?

新提案:defer 代码块

最近大家也在探讨一个与之相干的 Go 提案《proposal: Go 2: deferred code blocks》,由 @Damien Lloyd 提出,想看看有没有机会把 defer 的新语法落地。

原作者在应用 defer 时也是常常:

defer f()

但这样就无奈取得返回值。最终要变成:

defer func() {result := f()
   // do something with result
}()

基于上述相似的起因,想引入如下具备 defer 作用的代码块语法:

defer {// 在关闭函数的开端执行此操作}

在应用了 defer 关键字的函数最初执行这整个代码块 {...}。代码块中的每一行将按程序运行。

作者给出的代码示例:

func fn() {f, err := os.Create("eddycjy.txt")
    if err != nil {panic(err)
    }

    defer {err := f.Close()

       if err != nil {panic(err)
       }
    }
}

在 fn 函数,申明了 defer {...},代码块内是对 f.Close 的兜底判断和异样抛出。在函数完结后执行这整个代码块。

拥护的声音

当然,这看着仿佛是比拟美妙的。看起来原提案作者只是简化了 defer 是的闭包应用,调整了作用域的范畴。

但在社区内其实蒙受比拟多的拥护声音。蕴含但不限于:

1、 收益比不高 :这个提案只是防止了 func()() 等闭包申明,然而却要减少新的 defer 语法(语言语法更改会带来昂扬老本),这个变更的 ROI 不高。

2、 毁坏兼容性 :原 defer 关键字调用总是会跟着函数的词法调用,有良好的一致性。如果进行批改,会产生新的费解,毁坏一致性。也会对现有的许多工具(例如:动态剖析工具)产生影响,全要改。

3、 作用域问题 :本来 defer func{}() 的代码块构造下,你的代码作用域都限于闭包函数下。而应用新的 defer {} 的构造,该返回和操作,是否应该会影响到内部函数的后果?(这是最有争议的一点,作者也比拟前言不搭后语,没明确指明语法意思)

总结

一开始乍一眼一看,感觉只是把 defer 关键字语句简化一下,如同特地好,省了几个单词。就像 if err != nil 也会有提要用 Rust 的 ? 等用法来代替的。

通过社区网友们指出后,发现这里猫腻不少。一门曾经有 10+ 年的编程语言,还有 Go1 兼容性保障的。做出这类带作用域的提案变更,是有比拟大的危险的。

同时对于 Go 工具链的影响,也是十分大的。一改,间接都完犊子了。的确须要尽量沉思。原作者齐全没提到。

该提案,正在凋谢 3 周期待意见收集。很神奇,没更多的人谈话,但提案的表情给了很多个不认同。

举荐浏览

  • Go 团队将批改 for 循环变量的语义,Go1.21 新版本即可体验!
  • Go1.21 速览:Context 能够设置勾销起因和回调函数了,等的可太久了!
  • Go1.21 速览:过了一年半,slices、maps 泛型库终于要退出规范库。。。
退出移动版