Junedayday Blog

六月天天的个人博客

0%

【每周小结】2023-Week11

Go技巧 - Variable Shadowing

Go 语法中有一个较为隐蔽的特性 - Variable Shadowing,常被翻译为 变量隐藏。官方对这块提供的资料很少,经常有不少Go语言开发者在这块“踩雷”。

语法意义 - 作用域

既然Go官方支持这个特性,那就有对应的语法意义,简而言之就是 控制变量的作用域

下面是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
x := 0

// value - 0
fmt.Println(x)

if true {
// variable shadowing
x := 1
x++
// value - 2
fmt.Println(x)
}

// value - 0
fmt.Println(x)
}

而其中的x := 1是创建了一个新的变量,它的作用域仅在 if true之后的括号内。当离开作用域之后, x变量又回到了原值。

一个隐蔽的错误示例

上述示例可以将 x := 1 修改为 x = 1,或者重新命名变量x,就可以避免引入 variable shadowing

但在实际开发中,常常出现一些隐蔽的case:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main() {
f, err := foo()
_ = f

// variable shadowing
if b, err := bar(); err == nil {
_ = b
}

// nil
fmt.Println(err)
}

func foo() (string, error) {
return "", nil
}

func bar() (string, error) {
return "", errors.New("bar")
}

在多变量返回时,很容易出现variable shadowing的情况。例如,上述示例中的b, err := bar() ,有两个返回值:

  • b ,前文未定义,作用域仅在对应的括号中,不会出现问题
  • err,前文已定义,这里发生了variable shadowing,所以在括号内外是两个变量

由于Go语言推荐将error作为最后一个返回值,所以往往在error这个变量上会发生variable shadowing,常被称为error shadowing

尽管官方支持这种语法,但很容易发生预期不一致的结果。社区也提供了相关的工具shadow,可用下方的命令快速体验:

1
2
3
4
5
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
# 普通模式
shadow ./...
# 严格模式
shadow -strict ./...

用工具可以发现问题,但如何解决呢?我推荐两个思路:

思路一:移除:=赋值使用

variable shadowing常出现的场景是多变量返回,并且同时存在已定义与未定义的变量。那么,我们可以尝试提前定义所需的变量,移除:=的赋值方式。例如将示例改造为:

1
2
3
4
var b string
if b, err = bar(); err == nil {
_ = b
}

这种提前定义的方式会增加代码量,但它不仅避免了variable shadowing,还有一个比较大的优势:提高可读性

一行简单的变量定义,尤其是当定义的内容是一个复杂对象时,我们能从这个定义中读到很多内容,如一个var fe foo.Example,让变量简写fe和变量的定义foo.Example结合在一起,非常清晰。

思路二:重命名

重命名时,我们对变量有两种处理方式,以下面的errB为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
var err error

// 1. 变量仅被用于括号内,被丢弃
if b, errB := bar(); errB == nil {
_ = b
_ = errB
}

// 2. 变量被用于括号内外,赋值给外部变量err
if b, errB := bar(); errB == nil {
_ = b
err = errB
}

个人推荐方案

1
2
3
4
5
6
7
8
9
10
11
12
13
// 变量的生命周期 - 仅在括号内
// 重命名
if b, errB := bar(); errB == nil {
_ = b
_ = errB
}

// 变量的生命周期 - 括号内外
// 移除:=
var b string
if b, err = bar(); err == nil {
_ = b
}

以上是我个人的一种编程习惯,能减少因 variable shadowing 发生错误的概率,希望能帮助到各位同学。

编程思考 - 表述不清晰,往往是因为掌握得不够深入

在写方案、查问题、沟通业务时,开发者们经常有种感觉 - 我已经表达得很清晰了,但别人常常无法听懂。

我也经常遇到这种情况。以前,我会觉得是对方的问题,如对相关领域的知识储备太少;而现在,我更倾向于是自己对这块内容的掌握度仍不够,导致无法通俗易懂地表达。

我的思维主要有两点转变:

  1. 大师能面向不同人群、通俗易懂地讲清深奥的知识;
  2. 改变优先从自身出发,而不是寄希望于他人的主动变化;

当收到他人对你表述不清晰的评价时,不要过于抵触,不妨趁这个机会再深入研究相关知识点,提升自己的水平。

工作生活 - 你看到的,是你想看到的

最近读完了《拆掉了思维里的墙》,我印象最深的一句话是:你看到的,是你想看到的

这个世界可能是客观的,但每个人的世界都是主观的。在事前或者事后,很多人都清楚该以什么样的态度面对世界,理论上很完美;但事到临头,面对带有负面内容的事物,我们往往会不自觉地以自己最习惯的方式来抵抗、躲避。

如果你想看到一个自己理想中的世界,往往需要一定的锻炼,形成“肌肉记忆”,让自己的心态变得更强壮。

Github: https://github.com/Junedayday/code_reading

Blog: http://junes.tech/

Bilibili: https://space.bilibili.com/293775192

公众号: golangcoding

二维码