go 入门学习 了解代码块与作用域以及应对变量遮蔽问题的处理

代码块与作用域


func foo() { //代码块1
    { // 代码块2
        { // 代码块3
            { // 代码块4

            }
        }
    }
}

显示代码块

如上代码实例 方法 后大括号包含所有的内容 称作代码块,go语言支持代码块的嵌套,无代码的代码块 成为空代码块,

这种肉眼可以看到的代码块 我们成为 显示代码块,除了显示代码块还有隐式代码块

隐式代码块

在这里插入图片描述
隐式代码块分为从大到小分为 宇宙代码块 ,包级别代码块, 文件代码块 ,控制语句层面代码块 例如 if,for, switch, 以及控制语句代码块中得 case,default 每个控制语句对应的子句都对应的某一代码块

宇宙级别代码块

go语言最大的 代码块 ,主要包含 go语言 设定的 变量 类型 比如 int , string
常量 true false 函数 make ,panic ,他可以在任何go文件中使用 ,这些内定的 预定义标识符 是宇宙级别代码块

包级别代码块

对应没一个包中 声明的常量 变量 函数 不包括方法 对应的标识符的作用域是包代码块。

对于作用域为包代码块的标识符,我需要你知道一个特殊情况。那就是当一个包 A 导入另外一个包 B 后,包 A 仅可以使用被导入包包 B 中的导出标识符

按照 Go 语言定义,一个标识符要成为导出标识符需同时具备两个条件:一是这个标识符声明在包代码块中,或者它是一个字段名或方法名;二是它名字第一个字符是一个大写的 Unicode 字符。这两个条件缺一不可。

文件代码块

那就是当一个包 A 导入另外一个包 B
导入包名的作用域是文件块作用域

控制语句层代码块
//例如 switch 语句中switch 关键词后面 大括号包含的代码块
switch {

}
控制语句子句代码块
//例如 switch 语句中子句中 case,default 关键词:后面的代码块
switch {
    case xxx:
    defalt:
}
代码块中对应工作运行的范围,代码块对应的作用域

变量遮蔽问题

变量遮蔽问题产生的原因

就是内层代码块中声明了一个与外层代码块同名且同类型的变量,这样,内层代码块中的同名变量就会替代那个外层变量,参与此层代码块内的相关计算,我们也就说内层变量遮蔽了外层同名变量

package main

import (
	"errors"
	"fmt"
)

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("it is err ............", err)
	}
	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()
	fmt.Println("err ............", err)
	fmt.Println("nil ............", nil)

	if err != nil {
		fmt.Println("call checkYear error:", err)
		return
	}
	fmt.Println("call checkYear ok")
}

PS D:\coder\goprojects\codescope> go run .\main.go 
it is 2021
it is err ............ <nil>
after check, it is 2020
err ............ wrong year
nil ............ <nil>
call checkYear error: wrong year

首先getYear 设置返回 2021,结果输出了 after check, it is 2020, err != nil
输出了 call checkYear error: wrong year err 的值变成 wrong year ,这都是变量屏蔽问题导致的

问题1 遮蔽预定义标识符

type new int 这一行,重新定了new 预定义标识符,虽然执行结果异常不是他导致的,我们在定义的时候不要使用这些预定义的标识符,以防造成难以排查的问题

问题2 遮蔽包代码块中的变量
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("it is err ............", err)
	}
	fmt.Println("after check, it is", a)
	return err
}

顶部已经声明了 变量名为 a的包级别变量,又重新在 checkYear 方法中得 switch 语句中 短变量方式声明了a变量 导致变量屏蔽问题,短变量方式声明了a变量他的作用域只在switch 代码块里面,他从getYear 方法 获得赋值 2021 执行 main包的时候输出了 it is 2021,也因为短变量 声明a 作用域是在 switch 里面 不能修改 顶部包级别变量 var a 的值,导致了输出 after check, it is 2020

问题3 遮蔽外层显式代码块中的变量

我们看到输出 call checkYear error: wrong year 代码中 err != nil 走到了异常的输出内容里面 ,此时 err 的值 为 wrong year

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("it is err ............", err)
	}
	fmt.Println("after check, it is", a)
	return err
}

checkYear 方式中先是 定义了 err := errors.New(“wrong year”) 他的作用域是
checkYear {
}
switch 再一次声明了 err switch a, err := getYear(), 他声明变量的作用域是
switch {

}
switch 声明的 err 不会修改 首次定义声明的 year 所以renturn 返回的是 wrong year

使用 go vet 工具检测 变量屏蔽的问题
[root@VM-16-13-centos codescope]# go vet -vettool=$(which shadow) -strict main.go
# command-line-arguments
./main.go:13:12: declaration of "err" shadows declaration at line 11

go vet 也只是找到了 err 这一个 变量屏蔽的问题,预定义修饰符 new 和 变量a 的问题也没检测到

还是我们只有了解变量遮蔽问题本质,在日常编写代码时注意同名变量的声明,注意短变量声明与控制语句的结合,才能从根源上尽量避免变量遮蔽问题的发生。

好记性不如烂笔头 本文学自 极客时间 Tony Bai · Go 语言第一课

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值