【问题标题】:Using the variable on range scope `x` in function literal (scopelint)在函数文字(scopelint)中使用范围范围`x`上的变量
【发布时间】:2021-10-04 03:30:35
【问题描述】:
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 给我一些问题:

  • 客户端 := fake.NewSimpleClientset(x.objs...)
  • 实际 := getUID(client, x.namespace)
  • assert.Equal(t, x.expected, actual)

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

【问题讨论】:

  • 您可以通过在循环内创建x 的副本来使lint 开心,即在t.Run(... 上方添加x := x
  • 嗨@mkopriva,你能解释一下原因吗?
  • @GiuseppePercuoco 很好,原因与 icza 解释的相同。当您在循环内执行x := x 时,您将声明一个 变量x 并将旧变量x 的值分配给它。 linter 检测到这一点并知道循环内任何引用 x 的代码都引用了当前迭代本地的新 x

标签: unit-testing go


【解决方案1】:

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

所以go vet 警告此类用途。

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

x2 := x

并在函数文字内引用x2。这会让go vet 开心。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-12
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 2013-04-01
    • 1970-01-01
    相关资源
    最近更新 更多