在函数文字中使用范围范围“x”上的变量 (scopelint)

Giu*_*oco 5 unit-testing go

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)
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

Lint 给我一些问题:

  • 客户端 := fake.NewSimpleClientset(x.objs...)
  • 实际 := getUID(客户端, x.namespace)
  • 断言.Equal(t, x.预期, 实际)

它报告此错误:“在函数文字中的范围范围内使用变量x(scopelint)

icz*_*cza 14

x是在每次迭代中重用的循环变量。然后您创建一个传递给的函数文字t.Run()。编译器不知道(没有保证)t.Run()返回后是否不调用创建和传递的函数文字,在这种情况下,函数文字将引用循环变量,该变量将被下一次迭代的值覆盖。这很少是\xe2\x80\x93(如果曾经\xe2\x80\x93)的意图。如果函数文字在另一个 gorotuine 中同时执行,这通常是令人讨厌的错误的根源,甚至是数据竞争。

\n

因此go vet警告此类用途。

\n

通常的解决方案是将循环变量的作为参数传递给函数文字,或者创建循环变量的副本并引用该副本。由于函数文字的签名是固定的(您无法更改它),因此创建变量的副本,例如:

\n
x2 := x\n
Run Code Online (Sandbox Code Playgroud)\n

并引用x2函数文字内部。这会让人go vet高兴。

\n

另外,由于制作副本的意图很明确,因此您可以使用相同的名称,例如x := x,哪个副本将隐藏循环变量。在上面的简短变量声明之后,标识符x将引用本地副本(而不是循环变量)。一般来说,这可能会引起混乱,但这里的意图是明确且可以接受的。

\n