首页 > 解决方案 > Go 切片变异最佳实践

问题描述

要知道底层的原始数组是否发生了变异,或者当切片被传递时,原始数组的副本是否发生了变异,这并不是很容易预测

a = [3]int {0, 1, 2}
s = a[:]
s[0] = 10
a[0] == s[0] // true


s = append(s, 3)
s[0] = 20
a[0] == s[0] // false

假设今天我有这种处理

a = [3]int {0, 1, 2}
s = some_func(a[:]) // returns slice
process(s) // a is getting mutated because so far some_func hasn't caused the underlying array to be copied

现在明天

a = [3]int {0, 1, 2}
s = some_func(a[:]) // returns slice, does append operations
process(s) // a is not getting mutated because some_func caused the underlying array to be copied

那么切片的最佳实践是什么?

标签: goslice

解决方案


如果一个函数确实修改了切片的底层数组,并承诺总是修改底层数组,那么该函数通常应该按值获取切片参数而不返回更新的切片:1

// Mutate() modifies (the backing array of) s in place to achieve $result.
// See below for why it returns an int.
func Mutate(s []T) int {
    // code
}

如果一个函数可以修改底层数组但可能返回一个使用新数组的切片,则该函数应该返回一个新的切片值,或者获取一个指向切片的指针:

// Replace() operates on a slice of T, but may return a totally new
// slice of T.
func Replace(s []T) []T {
    // code
}

当这个函数返回时,你应该假设底层数组,如果你持有它,可能会或可能不会被使用:

func callsReplace() {
    var arr [10]T
    s := Replace(arr[:])
    // From here on, do not use variable arr directly as
    // we don't know if it is s's backing array, or not.

    // more code
}

Mutate()承诺会修改数组。请注意,Mutate通常需要返回实际更新的数组元素的数量:

func callsMutate() {
    var arr [10]T
    n := Mutate(arr[:])
    // now work with arr[0] through arr[n]

    // more code
}

1当然,它可以使用一个指向数组对象的指针,并就地修改数组,但这不太灵活,因为数组大小随后被烘焙到类型中。


推荐阅读