Go语言中应避免包名与变量同名导致的shadowing(变量遮蔽)问题吗?
在testify这类测试库的测试代码中,存在大量包名被局部变量遮蔽的示例,例如:
import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestSomething(t *testing.T) { assert := assert.New(t) require := require.New(t) var a string = "Hello" var b string = "Hello" assert.Equal(a, b, "The two words should be the same.") }
用局部变量覆盖包命名空间的行为难道不该是需要明确决策、且极少出现的情况吗?以下是我认为更合理的写法示例:
import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestSomething(t *testing.T) { assertions := assert.New(t) req := require.New(t) var a string = "Hello" var b string = "Hello" assert.Equal(a, b, "The two words should be the same.") }
我认为合理的做法是给包起别名(比如pkgassert,不过我觉得这种写法不够惯用),或者确保变量名不与包名重名,比如使用assertions := assert.New(t)的写法。
其他同类示例:
- 合规:
buf := bytes.Buffer{} - 不合规:
bytes := bytes.Buffer{}
注:我并不纠结变量命名模式,因为Go通常建议在窄作用域中使用单字母或短变量名,此处假设因函数长度原因,使用更长的变量名是可接受且符合需求的。
如果这种遮蔽包名的做法在测试包中是被推崇的,我想了解具体原因,它看起来违反了Go惯用写法中“无魔法”、高可读性的核心原则。我也想知道这种做法是否存在不良副作用,我的猜测是:发生遮蔽后,同一作用域内后续对原包的调用都会失效。
编辑补充
《Scope Shadowing in Go》是一篇很好的文章,给出了很多关于变量遮蔽使用的优秀观点,我从这篇文章和Stack社区评论中得到的核心收获有两点:
- 它确实可以提升可读性(我认为这可能是因为Go推崇简洁短小的包名,包名长度灵活)。
- 并行测试问题是我之前看过的内容,当时我不理解为什么推荐通过如下写法“隐藏”循环变量:
for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() // Here you test tc.value against a test function. // Let's use t.Log as our test function :-) t.Log(tc.value) }) }
现在我对变量遮蔽有了更深的理解,才明白这种“隐藏”本质是在循环内部赋值,遮蔽外层作用域的tc变量。这看起来是个不错的使用场景,但我仍然认为如果不加注释的话,这种写法属于“炫技”,违反了Go清晰可读、无“魔法”的惯用设计目标。
内容的提问来源于stack exchange,提问作者sheldonhull
相关产品推荐
相关产品推荐

