11|代码块与作用域:如何保证变量不会被遮蔽?
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
Go语言中的变量遮蔽问题及解决方法是本文的核心内容。文章通过深入探讨代码块和作用域的概念,清晰地阐述了变量遮蔽问题的根本原因以及如何避免这一问题。在Go语言中,代码块支持嵌套,而作用域是针对标识符的,每个标识符都有自己的作用域。通过对代码块和作用域的深入理解,开发人员可以彻底避免变量遮蔽问题。文章还介绍了Go官方提供的go vet工具,可以用于检测变量遮蔽问题,但也指出了工具的局限性,强调了深入理解代码块与作用域概念的重要性。总的来说,本文内容简洁明了,适合读者快速了解变量遮蔽问题及解决方法,为编码提供指导和帮助。文章通过深入的技术讨论和实例分析,为读者提供了对Go语言中变量遮蔽问题的全面理解和解决思路。
《Tony Bai · Go 语言第一课》,新⼈⾸单¥59
全部留言(48)
- 最新
- 精选
- lesserror关于这一块儿的知识其实还挺绕的。 不同代码块中的重名变量与变量重声明中的变量区别到底在哪儿?为了方便描述,我就把不同代码块中的重名变量叫做“可重名变量”吧。注意,在同一个代码块中不允许出现重名的变量,这违背了 Go 语言的语法。关于这两者的表象和机理,我们已经讨论得足够充分了。你现在可以说出几条区别?请想一想,然后再看下面的列表。 1. 变量重声明中的变量一定是在某一个代码块内的。注意,这里的“某一个代码块内”并不包含它的任何子代码块,否则就变成了“多个代码块之间”。而可重名变量指的正是在多个代码块之间由相同的标识符代表的变量。 2. 变量重声明是对同一个变量的多次声明,这里的变量只有一个。而可重名变量中涉及的变量肯定是有多个的。 3. 不论对变量重声明多少次,其类型必须始终一致,具体遵从它第一次被声明时给定的类型。而可重名变量之间不存在类似的限制,它们的类型可以是任意的。 4. 如果可重名变量所在的代码块之间,存在直接或间接的嵌套关系,那么它们之间一定会存在“屏蔽”的现象。但是这种现象绝对不会在变量重声明的场景下出现。
作者回复: 👍
2021-11-05711 - Amosヾ可不可以通过变量尽量不重名来避免变量遮蔽呢?
作者回复: 不重名肯定不会遮蔽。但是实际编码中,一些常用的功能变量,比如表示错误的err、表示下标的i,表示key和value的k、v等,如果要做不同命名,很容易在代码中出现大量的k1,v1,k2,v2等,阅读起来总是感觉缺少了一些优雅感。不知你是否有同感。
2021-11-0549 - 扣剑书生func checkYear() error { err := errors.New("wrong year") // 短变量形式,屏蔽了外层的包级变量 a,代替 其接收值 // err代替上面 的 err接收值 // 接收放在 switch 作用域外 a, err := getYear() switch a { case 2020: fmt.Println("哦哦哦it is", a, err) case 2021: fmt.Println("it is", a) err = nil } fmt.Println("after check, it is", a) return err }
作者回复: 👍
2021-12-0268 - 文经约定号包级别的变量用长的名字,越是局部的变量用越短小的名字,应该能够解决一大部分变量zhe遮蔽的问题。
作者回复: 也算是一个办法。前提是明确规则,且大家都遵守。这样才能在协作中,减少遮蔽问题的发生频度。
2021-11-127 - 程旭阳go1.17.1 `type new int`会报错: cannot assign new to a (type int) in multiple assignment cannot use new value as type int in assignment 修改为 `type new = int` 之后不再报错 思考题解决方法: package main import ( "fmt" "errors" ) var a int = 2020 func checkYear() error { err := errors.New("wrong year") switch a, err = getYear(); a { case 2020: fmt.Println("it is", a, err) case 2021: fmt.Println("it is", a) err = nil } fmt.Println("after check, it is", a) return err } type year = int func getYear() (year, error) { var b int16 = 2021 return year(b), nil } func main() { err := checkYear() if err != nil { fmt.Println("call checkYear error:", err) return } fmt.Println("call checkYear ok") }
作者回复: 正确✅
2021-11-0636 - 子杨「作者回复: 一旦“禁止用预定义标识符定义新类型或者变量的行为”,那么new这样的预定义标识符就和关键字没啥区别了。」 想请问老师,预定义标识符和关键字的区别是啥?
作者回复: 预定义标识符可以被重新定义。 比如 var new int = 5 这时new就是一个变量。 但关键字不可以做标识符。 你不能用for作为变量名: var for int = 5 // error
2022-12-13归属地:辽宁5 - 615刚细翻看了下go语言圣经,switch里的a和err确实是新声明的而不存在赋值行为,因为重新赋值行为只存在于变量已经在相同block中声明过。
作者回复: 正解✅
2023-04-29归属地:北京3 - 🐎感觉和js一样,变量就近使用
作者回复: 其实这是一个编程通用原则,利于提升可读性
2022-08-21归属地:北京3 - Rayjun老师,这里我还有一个问题,我发现如果是在同级的作用域中,声明两个一样的变量就会报错,但是在不同级的作用域中就不会,那么 go 语言为何不禁止同名变量的声明,这样不就可以解决变量遮蔽的问题么
作者回复: 好问题!不过这是编程语言设计范畴的问题,我不是编程语言设计专家,不能从原理上给予解释。但从目前情况来看,似乎没有哪门编程语言禁止不同作用域的同名变量声明,显然这是一个语言设计与实现的惯例。
2022-03-013 - Rayjun修改两个地方,把 a 的类型改成 new,并 去掉 switch 那的一个引号 var a new = 2020 func checkYear() error { err := errors.New("wrong year") switch a, err = getYear(); a { case 2020: fmt.Println("it is", a, err) case 2021: fmt.Println("it is", a) err = nil } fmt.Println("after check, it is", a) return err } type new int func getYear() (new, error) { var b int16 = 2021 return new(b), nil } func main() { err := checkYear() if err != nil { fmt.Println("call checkYear error:", err) return } fmt.Println("call checkYear ok") }
作者回复: 有一点提醒一下:既然我们知道了new是预定义的标识符,我们在日常编写代码中尽量要避免重新定义new.
2021-11-063