在函数字面量中使用范围作用域变量`x`(scopelint)

huangapple go评论72阅读模式
英文:

Using the variable on range scope `x` in function literal (scopelint)

问题

func TestGetUID(t *testing.T) {
namespace := "lkfm"
expecteduid := "fake_uid"
var tests = []struct {
description string
expected string
namespace string
objs []runtime.Object
}{
{"PositiveScenario", expecteduid, namespace, []runtime.Object{simpleNamespace(namespace)}},
}

for _, x := range tests {
    t.Run(x.description, func(t *testing.T) {
        client := fake.NewSimpleClientset(x.objs...)
        actual := getUID(client, x.namespace)
        assert.Equal(t, x.expected, actual)
    })
}

}
Lint在以下代码上给我报了一些问题:

  • client := fake.NewSimpleClientset(x.objs...)
  • actual := getUID(client, x.namespace)
  • assert.Equal(t, x.expected, actual)

并且报了这个错误:"在函数字面量中使用了范围作用域变量 x (scopelint)"

英文:
func TestGetUID(t *testing.T) {
	namespace := "lkfm"
	expecteduid := "fake_uid"
	var tests = []struct {
		description string
		expected    string
		namespace   string
		objs        []runtime.Object
	}{
		{"PositiveScenario", expecteduid, namespace, []runtime.Object{simpleNamespace(namespace)}},
	}

	for _, x := range tests {
		t.Run(x.description, func(t *testing.T) {
			client := fake.NewSimpleClientset(x.objs...)
			actual := getUID(client, x.namespace)
			assert.Equal(t, x.expected, actual)
		})
	}
}

Lint give me some problem on :

  • client := fake.NewSimpleClientset(x.objs...)
  • actual := getUID(client, x.namespace)
  • assert.Equal(t, x.expected, actual)

and it reports this error : "Using the variable on range scope x in function literal (scopelint)"

答案1

得分: 14

x是循环变量,在每次迭代中被重复使用。你创建了一个函数字面量,并将其传递给了t.Run()。编译器无法确定在t.Run()返回后是否会调用创建并传递的函数字面量,如果调用了函数字面量,它将引用循环变量,而循环变量将被下一次迭代的值覆盖。这往往不是我们想要的结果,这种情况通常会导致严重的错误,甚至在另一个goroutine中并发执行函数字面量时可能导致数据竞争。

因此,go vet会对这种用法发出警告。

通常的解决方法是将循环变量的值作为参数传递给函数字面量,或者创建循环变量的副本并引用副本。由于你的函数字面量的签名是固定的(无法更改),你可以创建一个变量的副本,例如:

x2 := x

然后在函数字面量中引用x2。这样go vet就会满意了。

另外,由于复制变量的意图是明确的,你可以使用相同的名称,例如x := x,这个副本将隐藏循环变量。在上述的短变量声明之后,标识符x将引用局部副本(而不是循环变量)。一般情况下,这可能会引起混淆,但在这里意图是明确且可接受的。

英文:

x is the loop variable which is reused in each iteration. And you create a function literal which you pass to t.Run(). The compiler does not know (does not have guarantee) whether the created and passed function literal is not called after t.Run() returns, in which case the function literal would refer to the loop variable which will be overwritten with the value of the next iteration. This is rarely–if ever–the intention. It's often the source of nasty bugs, even data race if the function literal is executed concurrently in another gorotuine.

So go vet warns about such uses.

Often the solution is to pass the value of the loop variable to the function literal as an argument, or create a copy of the loop variable and refer to the copy. Since the signature of your function literal is fixed (you can't change it), create a copy of the variable, e.g.:

x2 := x

And refer to x2 inside the function literal. This will make go vet happy.

Also, since the intention of making a copy is clear, you may use the same name, e.g. x := x, which copy will shadow the loop variable. After the above short variable declaration, the identifier x will refer to the local copy (and not the loop variable). In general this may cause confusion, but here the intention is clear and acceptable.

huangapple
  • 本文由 发表于 2021年7月28日 19:29:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/68559574.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定