Go 探讨了13年,怎么解决再赋值的坑?
大家好,我是煎鱼。
最近在看 Go 的一些历史提案时,发现有个别很神奇的提案,已经提出来了许多年,但在如今依然没有关闭,并且不断地有人在讨论,但又解决不了。
有种 “很气又干不掉我的样子”,今天就由煎鱼带大家一起来看看是什么。
背景
今天本文介绍的 Go 提案《proposal: spec: various changes to :=[1]》是经典中的经典,初学者学习时常犯的问题。
该提案自 2009 年提出,最近一次的激烈讨论是 2021 年:
代码原型如下:
func f() (err os.Error) {
v, err := g()
if err != nil {
return
}
if v {
v, err := h()
if err != nil {
return
}
}
}
这段代码的问题在于函数片段中的 := 会导致产生一个新的 err 变量,该新变量会使得返回参数(err os.Error,也声明了 err)被覆盖。
也就是 Go 里的 := 重新赋值的逻辑,会导致参数被覆盖,从而引起隐藏问题,极其容易踩坑。
新提案
如开头所说,这是一个经过了 13 年,在 2022 年依然没有结局的提案。
煎鱼总结了提案和其他讨论的思路,社区一共提出了如下几种解决方案或思路。如下:
- 加语法糖。
- 干掉语法。
- 定规范。
加语法糖
这个想法删除了重新声明 := 语法,并添加了新的 : 和 :: 语法,用于新变量声明。
如下代码:
package bar
func foo() {
var x, err = f()
...
// 这里 “:err” 表示上面声明的 err。
var y, z, :err = g()
...
{
// 实际上,:err 表示代码区块里的已经声明的 err。
var w, :err = h()
...
// ::err 表示包级别声明的 err。
var u, v, ::err = j()
...
// 这个“err”是一个新的声明。
var m, n, err = k()
...
}
}
上述代码中给出了三种案例,分别是:
- var :err = x:表示最近一个作用域声明的 err,原意是指上面一个声明的 err,因此你会发现在代码区块和外,代表着不同的结果。
- var ::err = x:表示包级别声明的 err。
- var err = x:表示一个新的声明。
干掉语法
在另外一个提案《proposal: Go 2: let := support any l-value that = supports[2]》中 Go 语言之父 @ Rob Pike 直接表示想干掉 := 这个重新赋值的方式,而不是再修修补补,加一堆会更复杂。
如下图:
我认为我们应该以消除重新声明为目标,如果我们能够建立一个更平稳的错误处理模型,那么重新声明就变得不那么引人注目了。不过这不会很快发生。
删除功能而不是增加功能。
(大呼:less is more)
单行多次声明
首先修改重新赋值的语义,:= 左边的所有标识符总是被声明为新的变量,在同一个块内重新声明是不允许的。
如下代码:
a, err := foo()
b, err := foo() // 编译错误,因为 var err 已在此块中声明
第一行声明正常,第二行由于在同一个代码区块重新声明了,因此会出现编译错误,因为已经声明过了。
接着增加语法特性,允许在一行中混合使用 = 和 :=。如下代码:
// a 和 err 被声明和初始化(相当于:a, err := foo()
a:=, err:= foo()
// b 被声明和初始化,而 err 只被赋予了一个新值
b:=, err= foo()
if true {
// c 在 if 块中声明并初始化,并为 err 分配一个新值
c:=, err= foo()
}
if true {
// d 和 err 在 if 块中声明,err 被隐藏
d:=, err:= foo()
}
允许单行进行多次声明,本质上是明确了声明的范围,会提高代码可读性的复杂度。
总结
今天这篇文章给大家介绍了一个 13 年前(2009 年)就被发现的神坑。当初最早学习 Go 时,也碰到很多教程、文档,同学会遇到这个重新赋值声明的神坑。
实际上上述的 3 个方案,看起来是从不同的角度补全了这个重新声明的语法糖,但也加大了复杂度。
也许直接干掉,也可能是个不错的选择?
参考资料
[1]proposal: spec: various changes to :=: https://github.com/golang/go/issues/377
[2]proposal: Go 2: let := support any l-value that = supports: https://github.com/golang/go/issues/30318
相关文章
- Jgit的使用笔记
- 利用Github Action实现Tornadofx/JavaFx打包
- 叹息!GitHub Trending 即将成为历史!
- 微软软了?开源社区讨论炸锅,GitHub CEO 亲自来答
- GitHub Trending 列表频现重复项,前后端都没去重?
- Photoshop Elements 2021版本软件安装教程(mac+windows全版本都有)
- (ps全版本)Photoshop 2020的安装与破解教程(mac+windows全版本都有)
- (ps全版本)Photoshop cc2018的安装与破解教程(mac+windows全版本,包括2023
- 环境搭建:Oracle GoldenGate 大数据迁移到 Redshift/Flat file/Flume/Kafka测试流程
- 每个开发人员都要掌握的:最小 Linux 基础课
- 来撸羊毛了!Windows 环境下 Hexo 博客搭建,并部署到 GitHub Pages
- 超实用!手把手入门 MongoDB:这些坑点请一定远离
- 【GitHub日报】22-10-09 zustand、neovim、webtorrent、express 等4款App今日上新
- 【GitHub日报】22-10-10 brew、minio、vite、seaweedfs、dbeaver 等8款App今日上新
- 【GitHub日报】22-10-11 cobra、grafana、vue、ToolJet、redwood 等13款App今日上新
- Photoshop 2018 下载及安装教程(mac+windows全版本都有,包括最新的2023)
- Photoshop 2017 下载及安装教程(mac+windows全版本都有,包括最新的2023)
- Photoshop 2020 下载及安装教程(mac+windows全版本都有,包括最新的2023)
- Photoshop 2023 资源免费下载(mac+windows全版本都有,包括最新的2023)
- 最新版本Photoshop CC2018软件安装教程(mac+windows全版本都有,包括2023