在阅读strconv package时,我注意到对测试用例的枚举实现为:link
for i := range parseInt64Tests {
test := &parseInt64Tests[i]
if test.err != nil {
test.err = &NumError{"ParseInt", test.in, test.err}
}
}
我对&parseInt64Tests[i]
非常困惑。为什么不只是for i := range parseInt64Tests
?
我编写了自己的测试,以检查for循环是否返回原始数组项或新副本。
var calculationTests = []calculationCases {
{0,0, nil},
{8,0, nil},
{9,1,nil},
{11, 1, nil },
{12, 2, nil },
{14, 2, nil },
{1969, 654, nil },
{100756, 33583, nil },
}
func TestCalculate(t *testing.T) {
for i, test := range calculationTests {
if &calculationTests[i] != &test {
t.Errorf("items not equal %v %v %p, %p", test, calculationTests[i], &test, &calculationTests[i])
}
}
}
输出如下:
--- FAIL: TestCalculate (0.00s)
fule_test.go:25: array item not same {0 0 <nil>} {0 0 <nil>} 0xc0000a4040, 0x12306a0
fule_test.go:25: array item not same {8 0 <nil>} {8 0 <nil>} 0xc0000a4040, 0x12306c0
fule_test.go:25: array item not same {9 1 <nil>} {9 1 <nil>} 0xc0000a4040, 0x12306e0
fule_test.go:25: array item not same {11 1 <nil>} {11 1 <nil>} 0xc0000a4040, 0x1230700
fule_test.go:25: array item not same {12 2 <nil>} {12 2 <nil>} 0xc0000a4040, 0x1230720
fule_test.go:25: array item not same {14 2 <nil>} {14 2 <nil>} 0xc0000a4040, 0x1230740
fule_test.go:25: array item not same {1969 654 <nil>} {1969 654 <nil>} 0xc0000a4040, 0x1230760
fule_test.go:25: array item not same {100756 33583 <nil>} {100756 33583 <nil>} 0xc0000a4040, 0x1230780
FAIL
exit status 1
证明for loop
返回一个副本,而不是原始实例。
我的问题是:循环数组的最佳实践是什么,我应该始终使用索引器而不是枚举器吗?
您可以检查部分
使用对循环迭代器变量的引用
如上所述,&calculationTests[i]
是切片元素的地址。因此,如果将slice的地址与局部变量的地址进行比较,每次都会给出错误。如果您想比较这些值,则可以像
if calculationTests[i] != test {
t.Errorf("items not equal %v %v %p, %p", test, calculationTests[i], &test, &calculationTests[i])
}
我的问题是:循环数组的最佳实践是什么,我应该始终使用索引器而不是枚举器吗?
我认为这取决于您的用例。
在strconv
包的情况下,作者想要
modify全局范围内的parseInt64Tests
数组,因此他们选择使用索引号访问该数组的原始成员,然后对其进行修改。
parseInt64Tests
数组,因为枚举器返回该数组每个成员的副本;如果他们使用&
从该副本获取地址,它将返回另一个地址,而不是原始地址。通常,如果要修改或改变封闭变量(循环外的变量)的状态,请使用索引。如果没有,请使用枚举器。
这里是示例:https://play.golang.org/p/EItIKpWU_qK
package main
import (
"fmt"
)
type Data struct {
Value int
}
var modifyMe = []Data{
{Value: 0},
{Value: 1},
{Value: 2},
{Value: 3},
{Value: 4},
{Value: 5},
{Value: 6},
{Value: 7},
{Value: 8},
}
func main() {
// modify()
notModify()
fmt.Println(modifyMe)
}
func modify() {
for i, _ := range modifyMe {
refToModifyMeData := &modifyMe[i]
refToModifyMeData.Value = refToModifyMeData.Value + 100
}
}
func notModify() {
for _, data := range modifyMe {
refToModifyMeData := &data
refToModifyMeData.Value = refToModifyMeData.Value + 100
}
}